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);

57 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

      • DiNasty

      Can I at least have an answer ?
      even if it seems like a dumb question to you 🙂

      Thanks

    • Try using application/octet-stream as the MIME type if you want to force the save dialog.

    • 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).

    • 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??

Leave a Reply