204

I'm playing with the idea of making a completely JavaScript-based zip/unzip utility that anyone can access from a browser. They can just drag their zip directly into the browser and it'll let them download all the files within. They can also create new zip files by dragging individual files in.

I know it'd be better to do it serverside, but this project is just for a bit of fun.

Dragging files into the browser should be easy enough if I take advantage of the various methods available. (Gmail style)

Encoding/decoding should hopefully be fine. I've seen some as3 zip libraries so I'm sure I should be fine with that.

My issue is downloading the files at the end.

window.location = 'data:jpg/image;base64,/9j/4AAQSkZJR....' 

this works fine in Firefox but not in Chrome.

I can embed the files as images just fine in chrome using <img src="data:jpg/image;ba.." />, but the files won't necessarily be images. They could be any format.

Can anyone think of another solution or some kind of workaround?

2
  • Current support is, unfortunately, rather limited
    – Casebash
    Commented Mar 7, 2013 at 6:38
  • Yes, indeed, in 2013-03-07 is so.
    – Carson
    Commented Jul 15, 2021 at 15:13

15 Answers 15

296

If you also want to give a suggested name to the file (instead of the default 'download') you can use the following in Chrome, Firefox and some IE versions:

function downloadURI(uri, name) {
  var link = document.createElement("a");
  link.download = name;
  link.href = uri;
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
  delete link;
}

And the following example shows it's use:

downloadURI("data:text/html,HelloWorld!", "helloWorld.txt");
12
  • 10
    You can simplify that with link.click() instead of your eventFire-function... jsfiddle.net/ARTsinn/Ezx5m
    – yckart
    Commented May 16, 2013 at 13:41
  • 3
    Nice solution. but what does the link become after ?
    – webshaker
    Commented Aug 23, 2014 at 17:00
  • 7
    The variable 'link' goes out of the scope at the end of the function (note we never added it to the dom) so will be garbage collected shortly after.
    – owencm
    Commented Aug 25, 2014 at 20:03
  • 9
    This only works in Chrome. If you want it to work in Firefox/IE look at the answer from @MartinoDino .
    – TrueWill
    Commented Jul 17, 2015 at 20:06
  • 1
    I tried with download.js, as suggested here and it seems to work. :)
    – joaorodr84
    Commented Jul 31, 2017 at 11:49
158

function download(dataurl, filename) {
  const link = document.createElement("a");
  link.href = dataurl;
  link.download = filename;
  link.click();
}

download("data:text/html,HelloWorld!", "helloWorld.txt");

or:

function download(url, filename) {
  fetch(url)
    .then(response => response.blob())
    .then(blob => {
      const link = document.createElement("a");
      link.href = URL.createObjectURL(blob);
      link.download = filename;
      link.click();
  })
  .catch(console.error);
}

download("https://get.geojs.io/v1/ip/geo.json","geoip.json")
download("data:text/html,HelloWorld!", "helloWorld.txt");

8
  • 1
    click event on non existant element :D no need for appendchild.
    – Zibri
    Commented Aug 27, 2017 at 13:04
  • 2
    yes. you can simply call a.click() after you have created element 'a', and then set a's href. Commented Jan 17, 2019 at 13:21
  • in the past calling a.click() directly would not work but it worket with the event. Now they both work.
    – Zibri
    Commented Mar 17, 2019 at 9:48
  • 2
    This works in most modern browsers, but I'd note that appending to the document then removing is necessary to support some older browsers.
    – owencm
    Commented Mar 22, 2019 at 15:22
  • 5
    Your second solution should be used whenever the dataUri becomes too big (depends on the browser, but Chrome does not accept Uri of multiple megabytes in my experience). See also stackoverflow.com/questions/38781968/… . Commented Jul 31, 2019 at 9:24
56

Ideas:

  • Try a <a href="data:...." target="_blank"> (Untested)

  • Use downloadify instead of data URLs (would work for IE as well)

5
  • 1
    @jcubic thanks for pointing out the dead link. The new download location seems to be github.com/dcneiner/Downloadify
    – Pekka
    Commented Mar 3, 2011 at 21:56
  • 1
    If you can't use Flash but are running a Java server-side component, you can use this: github.com/suprememoocow/databounce. It uses a Servlet to 'bounce' the data from client. It would be fairly easy to perform the same trick in other server side technologies, such as Python, ASP.NET etc Commented Dec 16, 2011 at 16:27
  • 1
    Is there a way to do it without flash? Data URL for all browsers but IE and some kind of ActiveX foo for IE? (This way I managed to play music without flash: HTML5 audio for all browsers but IE and ActiveX for IE.)
    – panzi
    Commented Jan 29, 2012 at 0:26
  • 1
    Downloadify expects flash player in the browser. If the user has no flash player, then file won't download Commented Aug 30, 2016 at 9:23
  • yeah, why not using a big javascript library instead of writing 10 lines of code...(and the crazy thing is this is the accepted answer) stackoverflow.com/a/45905238/236062
    – Zibri
    Commented May 16, 2023 at 13:13
31

Want to share my experience and help someone stuck on the downloads not working in Firefox and updated answer to 2014. The below snippet will work in both firefox and chrome and it will accept a filename:

  // Construct the <a> element
  var link = document.createElement("a");
  link.download = thefilename;
  // Construct the uri
  var uri = 'data:text/csv;charset=utf-8;base64,' + someb64data
  link.href = uri;
  document.body.appendChild(link);
  link.click();
  // Cleanup the DOM
  document.body.removeChild(link);
5
  • 4
    Unfortunately, it doesn't work in Safari. Safari doesn't seem to recognize the download attribute. Thanks anyways, this is as close as I can get at the moment. Commented Dec 23, 2014 at 20:34
  • this is causing the window to redirect to the href value for me in IE 11. prevetDefault(); does not stop the behavior in IE
    – b_dubb
    Commented Jun 1, 2016 at 18:06
  • 1
    Thanks, also if btoa is not defined (e.g. in node eslinted frontend project) const btxt = new Buffer(text).toString('base64'); const uri = 'data:text/csv;charset=utf-8;base64,' + btxt + ';' Commented Feb 15, 2017 at 13:28
  • 3
    deleting the link is pointless, it goes out of scope on the next line and will be garbage collected, it's not a valid target for delete.
    – macdja38
    Commented Dec 3, 2017 at 0:15
  • 2
    no need for document.body.appendChild... see my answer stackoverflow.com/a/45905238/236062
    – Zibri
    Commented Mar 16, 2019 at 10:41
24

Here is a pure JavaScript solution I tested working in Firefox and Chrome but not in Internet Explorer:

function downloadDataUrlFromJavascript(filename, dataUrl) {

    // Construct the 'a' element
    var link = document.createElement("a");
    link.download = filename;
    link.target = "_blank";

    // Construct the URI
    link.href = dataUrl;
    document.body.appendChild(link);
    link.click();

    // Cleanup the DOM
    document.body.removeChild(link);
    delete link;
}

Cross-browser solutions found up until now:

downloadify -> Requires Flash

databounce -> Tested in IE 10 and 11, and doesn't work for me. Requires a servlet and some customization. (Incorrectly detects navigator. I had to set IE in compatibility mode to test, default charset in servlet, JavaScript options object with correct servlet path for absolute paths...) For non-IE browsers, it opens the file in the same window.

download.js -> http://danml.com/download.html Another library similar but not tested. Claims to be pure JavaScript, not requiring servlet nor Flash, but doesn't work on IE <= 9.

2
  • download.js is pretty good. Just did a quick check on IE11, it works. Thanks a lot!
    – Ricky Jiao
    Commented May 19, 2016 at 2:57
  • The correct location of download.js is github.com/rndme/download
    – RWC
    Commented Oct 19, 2022 at 14:10
14

There are several solutions but they depend on HTML5 and haven't been implemented completely in some browsers yet. Examples below were tested in Chrome and Firefox (partly works).

  1. Canvas example with save to file support. Just set your document.location.href to the data URI.
  2. Anchor download example. It uses <a href="your-data-uri" download="filename.txt"> to specify file name.
2
  • 3
    canvas example leads to 404 Commented Oct 22, 2015 at 10:28
  • The download attribute is working for me, for a pdf data url in chrome and mobile safari.
    – bbsimonbb
    Commented Aug 1, 2018 at 14:08
8

Combining answers from @owencm and @Chazt3n, this function will allow download of text from IE11, Firefox, and Chrome. (Sorry, I don't have access to Safari or Opera, but please add a comment if you try and it works.)

initiate_user_download = function(file_name, mime_type, text) {
    // Anything but IE works here
    if (undefined === window.navigator.msSaveOrOpenBlob) {
        var e = document.createElement('a');
        var href = 'data:' + mime_type + ';charset=utf-8,' + encodeURIComponent(text);
        e.setAttribute('href', href);
        e.setAttribute('download', file_name);
        document.body.appendChild(e);
        e.click();
        document.body.removeChild(e);
    }
    // IE-specific code
    else {
        var charCodeArr = new Array(text.length);
        for (var i = 0; i < text.length; ++i) {
            var charCode = text.charCodeAt(i);
            charCodeArr[i] = charCode;
        }
        var blob = new Blob([new Uint8Array(charCodeArr)], {type: mime_type});
        window.navigator.msSaveOrOpenBlob(blob, file_name);
    }
}

// Example:
initiate_user_download('data.csv', 'text/csv', 'Sample,Data,Here\n1,2,3\n');
7

This can be solved 100% entirely with HTML alone. Just set the href attribute to "data:(mimetypeheader),(url)". For instance...

<a
    href="data:video/mp4,http://www.example.com/video.mp4"
    target="_blank"
    download="video.mp4"
>Download Video</a>

Working example: JSFiddle Demo.

Because we use a Data URL, we are allowed to set the mimetype which indicates the type of data to download. Documentation:

Data URLs are composed of four parts: a prefix (data:), a MIME type indicating the type of data, an optional base64 token if non-textual, and the data itself. (Source: MDN Web Docs: Data URLs.)

Components:

  • <a ...> : The link tag.
  • href="data:video/mp4,http://www.example.com/video.mp4" : Here we are setting the link to the a data: with a header preconfigured to video/mp4. This is followed by the header mimetype. I.E., for a .txt file, it would would be text/plain. And then a comma separates it from the link we want to download.
  • target="_blank" : This indicates a new tab should be opened, it's not essential, but it helps guide the browser to the desired behavior.
  • download: This is the name of the file you're downloading.
1
  • 3
    The only thing that this code downloads is the URI and save it in the donwload filename, not the file, neither using your jsfiddle.net link.
    – masterguru
    Commented Nov 16, 2022 at 4:02
6

If you only need to actually have a download action, like if you bind it to some button that will generate the URL on the fly when clicked (in Vue or React for example), you can do something as easy as this:

const link = document.createElement('a')
link.href = url
link.click()

In my case, the file is already properly named but you can set it thanks to filename if needed.

8
  • Thank you very much for the light sir but the problem is. It just redirects it to a new URL and the video starts to play. From yesterday to today what I figured Out is the problem is "disposable content type" ------HTTP/1.1 206 Partial Content Server: nginx/1.19.3 Date: Thu, 27 Oct 2022 08:17:34 GMT Content-Type: video/mp4 Content-Length: 18821 Last-Modified: Wed, 26 Oct 2022 12:44:55 GMT Connection: keep-alive ETag: "63592bc7-1c4985" Content-Range: bytes 1835008-1853828/1853829------ with which the server is not responding with that. Please share some views, How can I manage this?
    – ash
    Commented Oct 27, 2022 at 8:20
  • Or even If I download it with a Blob or file saver. It downloads only 122bytes of file.-------- var FileSaver = require('file-saver') var blob = new Blob([this.src_url], { type: 'video/mp4' }) FileSaver.saveAs(blob, 'hello world.mp4')------
    – ash
    Commented Oct 27, 2022 at 8:23
  • with Axios, it throws this error Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at redis-nfs/videos/be319-72e1-2e79-8dc3-bcef1/…. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 200
    – ash
    Commented Oct 27, 2022 at 8:26
  • One More thing on analyzing that file of 122bytes. I found out that it does not contain anything but link of the file which I want to make downloadable. Your help will be deeply appreciated please help sir.
    – ash
    Commented Oct 27, 2022 at 8:35
  • 1
    @ash please create a new question for that one.
    – kissu
    Commented Oct 27, 2022 at 9:06
2

For anyone having issues in IE:

dataURItoBlob = function(dataURI) {
    var binary = atob(dataURI.split(',')[1]);
    var array = [];
    for(var i = 0; i < binary.length; i++) {
        array.push(binary.charCodeAt(i));
    }
    return new Blob([new Uint8Array(array)], {type: 'image/png'});
}

var blob = dataURItoBlob(uri);
window.navigator.msSaveOrOpenBlob(blob, "my-image.png");

This code was originally provided by @Yetti on this answer (separate question).

2

Coming late to the party, if you'd like to use a function without using the DOM, here it goes, since the DOM might not even be available for whatever reason.

It should be applicable in any Browser which has the fetch API.

Just test it here:

// declare the function
function downloadAsDataURL (url) {
  return new Promise((resolve, reject) => {
    fetch(url)
      .then(res => res.blob())
      .then(blob => {
        const reader = new FileReader()
        reader.readAsDataURL(blob)
        reader.onloadend = () => resolve(reader.result)
        reader.onerror = err => reject(err)
      })
      .catch(err => reject(err))
  })
}

// simply use it like this
downloadAsDataURL ('https://cdn-icons-png.flaticon.com/512/3404/3404134.png')
  .then((res) => { 
    console.log(res) 
  })
  .catch((err) => {
    console.error(err) 
  })

0

Your problem essentially boils down to "not all browsers will support this".

You could try a workaround and serve the unzipped files from a Flash object, but then you'd lose the JS-only purity (anyway, I'm not sure whether you currently can "drag files into browser" without some sort of Flash workaround - is that a HTML5 feature maybe?)

0

We can able to download file depends on use case

  1. download file from URL
  2. downlaod file after upload using UI

Example:

<Grid
    container
    spacing={2}
    alignContent={'stretch'}
    className={classes.rowsGridPadding}
>
    {
        row.attachments.length > 0 ? (row.attachments.map((d, i) => {
            return <Grid item sx={{ alignItems: 'flex-start' }} key={`${d.name}${i}`}>
                <Chip {...{
                    label: d.name,
                    onClick: () => {
                        if (Object.keys(d).length < 2) {
                            env$.subscribe({
                                err: err => console.log('err', err),
                                next: res => {
                                    fetch(`${res.downloadFileUrl}?filename=${d.name}&accountId=${row.accountId}`) // download file from url
                                        .then(response => response.blob())
                                        .then(blob => {
                                            const link = document.createElement('a')
                                            link.href = URL.createObjectURL(blob)
                                            link.download = d.name
                                            link.click()
                                            addNotification({ message: `${d.name} downloaded successfully`, variant: Variants.SUCCESS })
                                        })
                                        .catch(err => {
                                            addNotification({ message: errorMessages[err.status] || errorMessages.FETCH_ERROR })
                                        })
                                }
                            })
                        } else {
                            const link = document.createElement('a')
                            link.download = d.name
                            link.href = URL.createObjectURL(d) // download file after uploaded using UI where d is uploaded file object
                            link.click()
                            addNotification({ message: `${d.name} downloaded successfully`, variant: Variants.SUCCESS })
                        }
                    },
                    onDelete: () => {
                        onAttachChange({
                            attachments: row.attachments.filter(f => f.name !== d.name),
                            rowIndex: rowIndex
                        })
                    }
                }} />
            </Grid>
        })) : <Grid item sx={{ alignItems: 'flex-start' }}>No Attachments Found</Grid>
    }
</Grid>
-1
export const downloadAs = async (url: string, name: string) => {
  const blob = await axios.get(url, {
    headers: {
      'Content-Type': 'application/octet-stream',
    },
    responseType: 'blob',
  });
  const a = document.createElement('a');
  const href = window.URL.createObjectURL(blob.data);
  a.href = href;
  a.download = name;
  a.click();
};
-1

You can use a clean code solution, inform your url in a constant, and set it as param of open method instead in object window.

const url = "file url here"

window.open(url)

2
  • 1
    Please don't post code-only answers. Future readers will be grateful to see explained why it answers the question instead of having to infer it from the code. And, since this is an old question, please explain how it compliments the other answers. Commented May 17, 2022 at 19:08
  • this way you cannot control the file name
    – Deian
    Commented Nov 23, 2022 at 2:30

Not the answer you're looking for? Browse other questions tagged or ask your own question.