Explain Codes LogoExplain Codes Logo

Is it possible to write data to file using only JavaScript?

javascript
file-handling
downloads
blob
Alex KataevbyAlex Kataev·Aug 14, 2024
TLDR

To write data to a file in-browser using JavaScript, we can utilize the power of Blob objects and an <a> element to trigger the download:

const textData = "Example text to save"; //Let's pretend this is the secret recipe for mom's spaghetti const blob = new Blob([textData], { type: 'text/plain' }); //Wrap it inside a Blob object, not too tight! const downloadLink = document.createElement('a'); //Create an invisible link, no one has to see it downloadLink.href = URL.createObjectURL(blob); //Point the link towards the blob downloadLink.download = "savedText.txt"; //Name it as you wish, or let grandma choose it downloadLink.click(); //Force a click, yes without moving a muscle! URL.revokeObjectURL(downloadLink.href); //Clean up after partying

This will create a file named "savedText.txt" with the content "Example text to save" that users can download immediately.

User Experience Enhancement

Prompt for Immediate Download

To automatically trigger the download without waiting for the user to click, we can synchronize with the browser’s frame rendering utilizing window.requestAnimationFrame:

window.requestAnimationFrame(() => { //Get in sync with the browser downloadLink.click(); //Then unleash the click });

Handling File Names

The download attribute suggests our preferred file name for users to organize their newly downloaded files:

downloadLink.download = "PreferredFileName.txt"; //Handle with file

Clean Up Object URLs

To avoid memory leaks when generating files, especially multiple files in one session, we need to revoke the object URLs right after the download is initiated:

downloadLink.addEventListener('click', (event) => { //Listen for the click setTimeout(() => { //After the party... URL.revokeObjectURL(downloadLink.href); //Clean up! }, 100); });

What about Non-text File Data?

JavaScript is flexible, it can create downloadable binary files, for example, saving a PNG image, with the correct MIME type and data:

//You thought JavaScript only handles text? Well, think again! const imageData = 'data:image/png;base64,...'; // Base64-encoded image data const blob = new Blob([imageData], { type: 'image/png' }); //...the rest is similar to the text file example above

Make your Download Handling Funky

Allow User-defined File Names

Enable users to name their files or fall back to a default:

//Be kind, let users name the files const fileName = userInput.value || 'defaultName.txt'; downloadLink.download = fileName;

Expedite Downloads with Data URI Scheme

If you opt for direct downloads without creating blobs, there's the data URI scheme:

//Data URI in action, older browser users may frown though! const dataUri = 'data:application/octet-stream,' + encodeURIComponent(data); downloadLink.href = dataUri;

Older Browsers Need Love Too

Don't ignore users of older browsers. Adapt methods according to the browser's capabilities:

//Be welcoming to IE users too if (window.navigator && window.navigator.msSaveOrOpenBlob) { navigator.msSaveBlob(blob, fileName); // For IE, do a little jiggy } else { downloadLink.click(); // for other browsers, business as usual! }

Handling Non-text Binary Data

Make sure to prepare your binary data properly (say, base64 decoded) before feeding it to the Blob monster:

const decodedImageData = atob(imageData); // Binary decoded image data const blob = new Blob([decodedImageData], { type: 'image/png' }); //...the rest is similar to the text file example above