How to Upload CSV’s using React, Rails and Fetch

Jonathan Mines
3 min readApr 12, 2018

--

While working on a project I ran into some trouble finding the right way to upload CSV files to my rails backend using a React frontend and fetch requests as the mechanism for posting. So once I figured out the right way to do that I figured I’d write a post for the next person who might come across this issue.

React Frontend

On your FrontEnd, you’ll need an Input component with a type of “file”. I’m using Semantic UI React components so I imported their Input component like so:

<Input
type="file"
ref={(input) => { this.filesInput = input }}
name="file"
icon='file text outline'
iconPosition='left'
label='Upload CSV'
labelPosition='right'
placeholder='UploadCSV...'
onChange={this.handleChange}
/>

You’ll also need a handleChange function that grabs the attribute containing the actual file. Generally, with an input you might find yourself grabbing event.target.value. In The case of CSV uploads, you’re instead looking for your uploaded file in the “files” attribute. Because the files attribute is an array and I’m only looking to upload a single file at a time, I just select the first file by giving the selector a [0] index like so.

handleChange = (event) => {
this.setState({
csv: event.target.files[0]
})
}

Once you have the file you want to upload to your backend you’ll need to make a post request. Here’s where things may be a little different from how you would normally format post requests. First, you’ll need to create an instance of a FormData and append the file and any other information you may want to send like so:

let formData = new FormData();
formData.append('file', csv);
formData.append('buys', buys)

From the documentation, FormData is simply:

provides a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the XMLHttpRequest.send() method. It uses the same format a form would use if the encoding type were set to "multipart/form-data".

So think of it as an object creator, and the result of its instantiation as an object. In my case, I wanted to send the csv itself, which I set to a key of “file” in the first append and I also wanted to send a boolean called “buys” which I set to the key “buys” in the second append.

Now I can format my post request. The options for my post request I created as follows:

let options = {
method: 'POST',
headers: {"Authorization": localStorage.getItem("token")},
body: formData
}

Notice that I do not include the typical ‘Content-Type’ and ‘Accept’ keys in my headers. In fact, the only reason I have a header at all is because I want to associate the incoming data with a particular user. You may not need to do that so there’s a good chance you won’t have headers at all. This is a crucial difference between a traditional post request and a post request with a CSV.

The rest of your request is standard:

fetch(`http://localhost:3000/api/v1/csvs`, options)
.then(resp => resp.json())
.then(result => {
alert(result.message)})

Rails Backend

On your backend, if you put a byebug in the action you’re trying to target in your fetch request you can see the incoming params of the file you’re sending.

There’s a couple interesting things to note about the file in the params.

#<ActionDispatch::Http::UploadedFile:0x007f8b8dae3998 @tempfile=#<Tempfile:/var/folders/bz/184lnygx3pg31h1kjlgmr41r0000gn/T/RackMultipart20180412-5373-1ojnv76.csv>, @original_filename="StoreDataWon.csv", @content_type="text/csv", @headers="Content-Disposition: form-data; name=\"file\"; filename=\"StoreDataWon.csv\"\r\nContent-Type: text/csv\r\n">

The attribute you’re most interested in is the tempfile. Using my params I can access that file using the following selectors:

params["file"].tempfile

Because this tempfile is just a temporary copy of your CSV you can do whatever you would normally do to a CSV on your backend. For example, adding .read to the end of your tempfile will produce the text that is your CSV file.

With that string you can use the CSV class to operate on your CSV, extract data, parse the text etc.

--

--