How to Send File Objects from a React Frontend to Django Backend

This is probably pretty common knowledge, but after running into a relatively frustrating event at work I figured I’d do a little write up so I can just look here in the future instead of frantically googling around…

I’ve finally embraced using the loader() and action() abilities of react-router-dom and while they simplify many things, other things can be a little trickier to work with (looking at you loading states!). On one of my projects users need to be able to upload an excel file and pass that to a series of Python scripts to parse and manipulate, which was trivial with my previous vanilla HTML and JS implementation being served by Django.

The Issue

A few user experience improvements resulted in a ton of javascript code that was getting painful to manage so the solution was to refactor it all into react components and use that for the frontend. When doing this, I thought it would be a great opportunity to further reduce the amount of code written by firing off the form that takes the excel file with react-router-dom’s action feature.

My initial attempts either sent the dreaded “[object Object]” payload or a blank {file: {}} – various Content-Type settings and parsing of the formData didn’t seem to do anything.

On the Django front some of the magic surrounding whether data was passed in the request.body, request.POST, or request.FILES locations only added to the frustrations.

The Solution

While relatively anticlimactic, the solution was pretty straightforward once I allowed myself to type it out and not keep trying things that ended up failing.

When a form (with any method other than GET) is sent with react-router-dom’s action the default submission is intercepted and sent to the frontend. Here we can access the request object which has a formData() method to access the data entered into the form. Unfortunately (or fortunately I guess) this formData is not equivalent to a regular JS formData object – creating an object with it requires JSON.stringify to send in a request .

To send a multipart form with an attached file my solution was simply to create a new formData() and attach everything to this:

export async function action({ request }) {
const formData = await request.formData();
const data = Object.fromEntries(formData);

// added this to add data to new formData object
const postData = new formData();
postData.append(“other-keys-here”, data.otherKeyValues);
postData.append(“file”, data.file);

await fetch(“example.com/api/endpoint”, {
method: “POST”,
// body: data ///////// what I had been intially trying
body: postData,
});

return redirect(“/“);
)

After this the file object showed up in the expected request.FILES dict on the backend where I could dump it into pandas and do what I needed with it.

It’s probably my lack of understanding with JS and react and this is all probably super intuitive, but dang did this stump me for a little bit.

Anywho, all working now and I can come back here the next time I try to send a file – have fun out there!

Leave a comment