Eli Grey

Saving generated files on the client-side

Have you ever wanted to add a Save as… button to a webapp? Whether you’re making an advanced WebGL-powered CAD webapp and want to save 3D object files or you just want to save plain text files in a simple Markdown text editor, saving files in the browser has always been a tricky business.

Usually when you want to save a file generated with JavaScript, you have to send the data to your server and then return the data right back with a Content-disposition: attachment header. This is less than ideal for webapps that need to work offline. The W3C File API includes a FileSaver interface, which makes saving generated data as easy as saveAs(data, filename), though unfortunately it will eventually be removed from the spec.

I have written a JavaScript library called FileSaver.js, which implements FileSaver in all modern browsers. Now that it’s possible to generate any type of file you want right in the browser, document editors can have an instant save button that doesn’t rely on an online connection. When paired with the standard HTML5 canvas.toBlob() method, FileSaver.js lets you save canvases instantly and give them filenames, which is very useful for HTML5 image editing webapps. For browsers that don’t yet support canvas.toBlob(), Devin Samarin and I wrote canvas-toBlob.js. Saving a canvas is as simple as running the following code:

canvas.toBlob(function(blob) {
    saveAs(blob, filename);
});

I have created a demo of FileSaver.js in action that demonstrates saving a canvas doodle, plain text, and rich text. Please note that saving with custom filenames is only supported in browsers that either natively support FileSaver or browsers like Google Chrome 14 dev and Google Chrome Canary, that support <a>.download or web filesystems via LocalFileSystem.

How to construct files for saving

First off, you want to instantiate a Blob. The Blob API isn’t supported in all current browsers, so I made Blob.js which implements it. The following example illustrates how to save an XHTML document with saveAs().

saveAs(
      new Blob(
          [(new XMLSerializer).serializeToString(document)]
        , {type: "application/xhtml+xml;charset=" + document.characterSet}
    )
    , "document.xhtml"
);

Not saving textual data? You can save multiple binary Blobs and ArrayBuffers to a Blob as well! The following is an example of setting generating some binary data and saving it.

var
      buffer = new ArrayBuffer(8) // allocates 8 bytes
    , data = new DataView(buffer)
;
// You can write uint8/16/32s and float32/64s to dataviews
data.setUint8 (0, 0x01);
data.setUint16(1, 0x2345);
data.setUint32(3, 0x6789ABCD);
data.setUint8 (7, 0xEF);
 
saveAs(new Blob([buffer], {type: "example/binary"}), "data.dat");
// The contents of data.dat are <01 23 45 67 89 AB CD EF>

If you’re generating large files, you can implement an abort button that aborts the FileSaver.

var filesaver = saveAs(blob, "video.webm");
abort_button.addEventListener("click", function() {
    filesaver.abort();
}, false);

81 Comments (add yours)

  • Wow, this is all very useful. Thanks!

    • Eric Bidelman

    Love this! Just an FYI though, Chrome 13 no longer needs –unlimited-quota-for-files flags for the filesystem API to work. Also, –allow-file-access-from-files should only be used for testing as it allows access to file:// and is a security risk.

    • Eric Bidelman
    • Wow, I didn’t know that! I’m going to look into adding that into FileSaver.js this instant. Also, I just experimented with it a bit and it looks like Chrome doesn’t follow the download property precisely when there isn’t a filename extension but a known media type is used (e.g. it saves foo.txt if the type is text/plain and the filename is only “foo”). I’d prefer if developers had more control over this. Other than that, this is perfect when combined with object URLs!

    • belltoy

    There is an error in Safari 5.1 for Mac:BlobBuilder.js:101
    RangeError: Maximum call stack size exceeded.

  • […] is van rá a W3C FileSaver interface személyében, de míg az megérkezik, használhatjuk az Eli Grey által készített FileSaver.js-t mindazon böngészőkben, amelyek valamilyen (specifikus) módon támogatják ezt a műveletet. […]

    • Thejbw

    Just wanted to say thanks! I’ve searched high and low all over the place for a *good* way to save images created by the user in a JS app. Everyone seems to simply rely on toDataUrl() which for a large canvas was unusably slow (tens of seconds to open the URL in a new window). Your method is fast and cross browser friendly.

    • Dylan Van Remmerden
  • This looks great. However you mention that the FileSaver interface will eventually be removed from the W3 spec, which makes me think this is not something I want to use if it’s going to be deprecated. What is the new or alternative way to save files from the web if not via the FileSaver interface? a.download links? Thanks!

    • Yian Zhang

    Can I use the lib in my webapp without pay?

    • Yes, it’s free to use and free to modify. Just make sure to keep the license at the top or the @source line if you want to minify it.

    • DiNasty

    Hi,

    Using Chrome 23, it actually dowload the image.

    Using Opera 12 / Safari 5.1 / FireFox 14, the image is opened in a new tab.

    Is there any chance that this works the same way ? like prompt for download instead of openning in a new tab ?

    Thanks in advance

    • Luka Hadžiegrić

    Does anyone know what’s support for this on some more recent mobile devices?

    • mamjed

    Does this script support IE 10?

    • nicearma

    Hello, nice tutorial, but i’m trying to do this in one page for mobile, but in google chrome seems not work fine, after do the click the browser stay with the download but nothing happen after, you know how to fix that? Thank anyway for the tutorial

    • markus sticker

    Hi,
    is it possible to save a file to a specified local path?
    thx
    Markus

    • tom

    Hi,

    When I use the code with Chrome and FireFox I get the expected behavior. However, when I use Safari, instead of saving a file a new tab is opened with the content. Is there any workaround I can use to support the same behavior with Safari?

    • Arun

    Thanks, this is absolutely amazing. I really appreciate you doing this and releasing it open source.

    • Anand

    A very helpful plugin. Thanks much for making this.
    -Anand

    • Man

    When i put a .png image in my canvas, and then when i download it, i get an empty .png not the actual image (Chrome v29). Any ideas??
    I’m kind of new on JS but Nice work!!

    • Stich2028

    Hi! I was wondering if i can specify what location the canvas or blob to be saved.

      • Chet

      Hi. were you able to save it in a specified location? thank you!

    • Patrik Karisch

    Is there a similar library for opening files?

    • Vikram Solanki

    Does file saveas javascript works with Sharepoint list? I tried

    and then tried access the fields and filesaveas but it did not work I guess.. My requirement is to create text file, so I found filesaver.js file.. and found it to be most simple and quikest method.. but its not working.. it does nto even gives me an error messages.. please help me to fix this..

    • luca rasconi

    I used it to “save as” a CSV file and all works fine. Thank you

    • jespa007

    It’s really useful! Thanks :)!

  • […] saveAs method should use the window.saveAs function, requiring a FileSaver shim to be in […]

    • Alex

    the demo doesn’t work in ie11. Or am I doing smth not right?

    • Susheel

    Demo is not at all working in ipad safari – i am trying to save text file which contains some data but when i click on save as button it just open new tab and display the data and not saving any file.

    Is this demo supported on ipad safari.

    • Roman Cheskidov

    Dear Eli,
    I read your article: http://eligrey.com/blog/post/saving-generated-files-on-the-client-side
    Really, looks great.
    I tried to use your code:

    var blob = new Blob([“Hello, world!”], {type: “text/plain;charset=utf-8”});
    saveAs(blob, “hello world.txt”);

    and get in Chrome console: “Uncaught ReferenceError: saveAs is not defined”

    What have I done wrong?
    Thanks for advance.
    Roman Cheskidov

    • felixwon

    In IE9, I used the code ‘canvas.toBlob(function (blob) { saveAs(blob, ‘report.png’); });’, but the ‘blob’ return null. I looked into the code of ‘canvas-toBlob.js’ and found the function ‘toBlob’ hit none of the conditions before ‘callback(blob);’. Does anyone know what’s the problem? Thanks! I already included the ‘Blob.js’ file.

    • w h

    Can this code save an image in a canvas as a jpeg? If so, what is the process? Thanks…

  • […] use Eli Grey‘s filesaver.js for the download […]

  • […] It isn’t supported by any browser right now. But there is a compatibility library called FileSaver.js that adds this function to most modern browsers (including Internet Explorer 10+). Internet […]

    • jah

    Is it possible to change directory of the file being saved?

  • […] It isn’t supported by any browser right now. But there is a compatibility library called FileSaver.js that adds this function to most modern browsers (including Internet Explorer 10+). Internet […]

  • […] It isn’t supported by any browser right now. But there is a compatibility library called FileSaver.js that adds this function to most modern browsers (including Internet Explorer 10+). Internet […]

    • Anil Singh
    • awesomesauce129

    So how do I implement this into my code? Can I reference this library from the website or do I have to save it to my computer and then distribute it to anyone whom I want to use the program? would something like this work?:

    Thanks for the help, I’m still kinda new to the whole programming thing and I’m just getting into javascript. Libraries don’t really make sense to me yet =P

      • Jim

      Actually it looks more like Paul Kinlan on that Google Developers site copied straight from this site. This article has an older date than the Google one, and the links on the Google page go to this site (eligrey.com).

      Pretty poor showing if that’s an actual Google Dev stealing someone else’s work and passing it off as his own (no source given and no mention of Eli Grey, just a straight copy and paste).

        • Christopher Sahnwaldt

        As of August 2016, the article on that Google Developers site is clearly attributed to Eli Grey. No mention of Paul Kinlan. I guess there was a mixup in June 2015.

    • Christine

    How do I set the origin? Save dialog displays “from: blob:”

      • John

      You can’t change (or mask) the download location/origin URI. It always shows the source of the download location.
      saveAs() starts a download using different methods like Data URI and Blob URI:

      Data URI – data:application/octet-stream;base64,XhUWjhHu21zAn8DH1ep0Me8PIj...

      Blob URI – blob:application/octet-stream/2bab9b3c-6039-4970-a78e-43c05721377b

      So the origin ends u being one of these!

    • John

    It is a damn shame that this functionality is not standard. It makes no sense that reading user files is standard, but not writing them (or at least providing a safe, secure way to “download” virtual data to disk).

    Maybe URL.createObjectURI( new Blob() ) will someday become the standard across all browsers. 🙂

    • anbeyon

    Hi Eli, Thanks for FileSave.JS. It is great!
    I am a bit of a newbie with JavaScript and the various differences between browsers. I have been using FileSaver.js on Windows with various browsers and have not had any problems.
    I am just wondering if someone was to using Linux with KDE and Firefox is there anything in FileSaver.js that would stop it working?
    or it is safe to say if it works in Firefox on Windows then it will be fine on Linux?

    • Isaac

    Is there anyway to add exif metadata to the canvas?

    • Diane Blackwood

    Your demo does not work in Safari 9.1. Do you know of a way to save files from javascript in Safarli 9?
    Thanks
    Diane

    • Lajpat

    Is there any way in which I can compress JPG & PNG before saving??

    • Mei
  • I am inserting this code into my application above. I does a great cross-browser job. I cannot figure out why file names that include “~” are replaced by “-“. This is true for all browsers except IE. When and where are the “~” being replaced by “-“? Thanks!

  • This is great, but I found that if the file name is something like:
    SaveAsFile(htmX,”filename~V~09082016.htm”,”text/plain;charset=utf-8″)

    Internet explorer, EDGE, Firefox save as filename~V~09082016.htm
    Chrome and Opera saves as filename-V-09082016.htm (replaces ~ for -)
    Do you have a solution for this? My application saves new data with a version date and it distinguishes the version portion after ~V~ (~ is not used much in file names text entry so ~ is useful to avoid confusion,

  • I forgot to mark the notification check boxes, pleas ignore my previous note

    This is great, but I found that if the file name is something like:
    SaveAsFile(htmX,”filename~V~09082016.htm”,”text/plain;charset=utf-8″)

    Internet explorer, EDGE, Firefox save as filename~V~09082016.htm
    Chrome and Opera saves as filename-V-09082016.htm (replaces ~ for -)
    Do you have a solution for this? My application saves new data with a version date and it distinguishes the version portion after ~V~ (~ is not used much in file names text entry so ~ is useful to avoid confusion,

    • Bernd

    Hi,
    just want to thank you for this fantastic script. It works very fine.
    Just one question:
    is it possible to eliminate the automatic adding of the BOM,
    ’cause I need it for generating text for further processing starting directly qith the first charakter …

    • Santosh

    Is there any option to have the borders to the downloaded excel?

    This is very useful when we have more than 4000+ rows.. Just wanted the borders between the cells of the excel sheet saved.

    • ElenaShao

    Hello , can I save the file to a designated path ? I want to definite the download path with custom config.
    Thanks.

    • Bhawna

    Hi Eli,

    I am using fileSaver for saving files to client side. I am successfully able to download txt file, but that whole content come in 1 line, can i predefine wrap in anyway?

    Another, i tried saving the file as XML but when i tried to open it with IE11. it didnt open, i mean the address bar was empty

    • Enzo

    Thanks a lot Eli !

    • Dov

    Hi Eli;

    FileSave.js saves beautifully into Excel/xls from an HTML table.
    Is there anyway to get images embedded in?
    Googled extensively. I tried, src=URI, Base64 and all I get is the broken image icon.

    Any help would be much appreciated.

    • Mark Roberts
      • Eli Grey

      You seem to be confusing the MIME media type charset parameter with the deprecated HTML charset property. The charset parameter referenced by this blog post is unrelated to the HTML charset property.

    • Mili Sharma

    I am trying to save my fabric.js canvas using your FileSaver.js, but I am getting “Uncaught DOMException: Failed to execute ‘toBlob’ on ‘HTMLCanvasElement’: Tainted canvases may not be exported” error.
    This is the code I used to save file:

    window.saveCanvas = function(){
    canvas.getElement().toBlob(function(blob){
    saveAs(blob, annotation.png);
    });
    }
    I have absolutely no idea whats going on. Would appreciate some guidance , as I am kind of stuck here.

    • Don

    Hi there! Quick question that’s toally off topic. Do you know hoow to make your site mobile friendly?
    My site looks weird when browsihg from my iphone 4.
    I’m trying to find a theme or plugin that might be able to correct this issue.
    If you have any suggestions, please share. Cheers!

    • Micah Epps

    Today, it works in Chrome, FF and IE 11. Nice work!

    saveAs(new Blob([‘hello world’], { type: “application/xml;charset=” + document.characterSet }), “Hello Save As.xml”);

    • Brad

    Getting this warning:
    FileSaver.min.js?21a6:1 [Deprecation] Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user’s experience. For more help, check https://xhr.spec.whatwg.org/.

    • wlei

    can i save file silently?

  • The demo at PrimeNG of ‘Table – Export’,
    https://primefaces.org/primeng/showcase/#/table/export
    uses FileSaver,

    We used similar code, however in Edge Browser when export, after a prompt to “Open, Save or Cancel”, click on OPEN opens file, then 2nd prompt appears on Edge browser “Open, Open Folder and Cancel”.

    While in both the demo at PrimeNG and at
    https://eligrey.com/demos/FileSaver.js/
    2nd prompt doesn’t appear on Edge browser.

    Could you help with how to achieve similar in the demos, without 2nd promt?

    Thanks

  • Tried saving a canvas into a blob and a blob into a PNG file inside the Android WebView of a mobile app and it is not working at all. If done with the web browser, it works like a charm, but the app is just showing a toast that reads: (((Can only download HTTP/HTTPS URIs))) followed by the image source text.

    I’m about to find impossible saving anything from the WebView from Javascript, since Google Play Store changed to API 28. My project has just stopped due to this fact. If you have a solution, please, I beg you to reply to my email address or my creepy site. Thanks in advance!

  • Cool, imma need this lol

    • Laoyao

    Can I use this script for commercial purposes?

      • Eli Grey

      Yes. You can do anything you want with this free software except hold me liable for your own use.

Leave a Reply