How to Create, Download, and Upload Files in React Apps
No server needed!
Rich single page applications (SPAs) built with React.JS, Angular, and other frameworks are becoming ever more popular. When an SPA app creates data, how can the user download the data, as a file, to the desktop?
Uploading too: how can the user upload files to the React.JS app for processing on the browser?
TL;DR: A working React.JS example via JSFiddle.
Creating and downloading a file from an application
For most web applications, files are downloaded from the server. The Content-type
and Content-Disposition
headers are used to declare the file type and suggest to the browser how the file should be handled, including a suggested file name.
In our case, we want to create or manage data using JavaScript within the SPA, then enable the user to download the data as a file. No server needed!
Here are the steps:
Choose and create an output format
JavaScript variables and structures (objects and arrays) are processed in a native binary format. For example, consider a JavaScript array containing the names of the US 2020 Election swing states (Pennsylvania, Florida, Ohio, Michigan, Arizona, North Carolina, Wisconsin, and Iowa). The array’s storage in memory is implementation-specific: Firefox’s memory layout for an array can be different from Chrome’s.
To output the swing states array as a file, first choose an output format. You could decide to output the states as a report in a text file, one line per state. You could choose to use a Comma-Separated Value (CSV) format. Or JSON, or XML. All of these formats are text formats.
You could choose a binary format such as Universal Binary JSON (UBJSON) or even the Excel binary file format. But it is difficult to manipulate binary in JavaScript. I recommend using text formats whenever possible.
Next, convert the native JavaScript data to the chosen output format.
For JSON, use the JSON.stringify
method. I always include an indent of three or four spaces to make the output readable. I think the value of being able to easily read the output outweighs the relatively low costs of larger output files.
While JSON supports the use of an array as the top-level object, it isn’t recommended to do so. Better to create the JSON as an object that includes the array: {"states": ["Pennsylvania", "Florida", … ]}
For CSV, multiple JavaScript generator implementations are available. XML is not hard to create either. But if you store it in the DOM, remember that you’ll need to convert the structure to a string before it can be written to a file.
Each of your scalar variables (variables that are not objects, arrays, or other structures) must have a string representation that can later be parsed when the file is read. This is simple and obvious for strings, integers, and real numbers.
For any other type of data, you must explicitly decide how to handle the data type. For example, JSON natively supports the NULL
data value. But CSV does not: should NULL
values be stored as empty strings, as the string "NULL"
, as the string "**NULL**"
, or as something else? It’s your choice. XML supports the NULL
value via the xsi:nil
attribute.
Dates and DateTime JavaScript objects are also an issue. A common answer is to convert the value to text using the ISO 8601 standard since it can later be parsed to re-create the date object. Pro-tip: remember to consider timezone issues.
Downloading the file
With HTML5, some prior complications are eliminated. Eg, the download
attribute of the anchor element is easily used to set the suggested filename. Here’s the pattern:
- The user initiates the download via a button connected to a JavaScript method.
- The data is converted to the output format. The result is a string.
- A
Blob
is created from the string. - An
Object URL
is created from theBlob
using theURL.createObjectURL
method. - A hidden anchor element’s
href
attribute is set to theObject URL
. - The anchor element’s
click
method is invoked. Normally theclick
method is invoked when the user clicks on the element. In this case, we programmatically click the element so the user only needs to initiate the download in step 1. - After the
click
method completes, theObject URL
can be freed.
A working React.JS example via JSFiddle is below. Click the Result tab.
Pro-tips
If you’re downloading your application’s internal state so it can be uploaded and restored in the future:
- Use JSON.
- Include a version attribute at the object’s root level to enable the data format to be updated in the future with the version of the data format easily recognized and parsed appropriately.
- Don’t try to “pickle” data in a binary format, convert it to a text format.
- Binary data such as images or PDF files can be stored within a JSON data structure by using BASE64 encoding.
Uploading a file to the app
Instead of uploading a file to the server, we can upload the file to the application running in the browser. The application can process the file locally. The app can further upload the file to the server via Ajax if desired.
Create a file input element in your app. In my JSFiddle example, I trigger the input element via a button with my preferred button text, but it is not necessary to do so.
Use the input element’s multiple
attribute to control whether the user can choose more than one file or not. Use the accept
attribute to control the types of files that can be picked. The MDN documentation provides additional information on setting these attributes.
Set the onChange attribute
to your handler method for the uploaded file or files. Here’s the file input element and the annotated handler:
Pro-tips
- Don’t make any assumptions about the contents of the uploaded file or files. For example, someone could rename a binary file or image “data.json”.
- In my example I’m using the
FileReader.readAsText
method. The newerBlob.text
method could also be used, but its browser support is not as complete as theFileReader
object.
Summary
It’s easy to generate downloads from within your browser-based code. No need to involve a server! Your SPA application can generate and download CSV files, JSON data, text reports and more.
Uploading files to your SPA application is also easy. Downloading and uploading files to a SPA application instead of a distant server can provide a faster and superior user experience for your users. Try it out with your next app.
Resources
- A working React.JS example via JSFiddle.
- HTML5 Blob
- HTML5 URL.createObjectURL