Small JS script to download files on Safari
New here? Learn about Bountify and follow @bountify to get notified of new bounties! x

I need a small JS piece of code to be able to download files (CSV, XML, Zip etc) to desktop from Safari. Currently I have some code which uses zip.js which works fine in Firefox when downloading files but for some reason this doesn’t work in Safari.

So I need a small piece of JS code that can be plugged into my existing code and can download files to desktop via Safari.

Thanks

Why do you need JS for this? What's wrong with a simple download link on the page?
henrebotha over 2 years ago
I'm dynamically generating content on the frontend using JS and I need users to be able to download this data. This is just a purely static HTML page with no server so I can't use ajax to send data to a server and creating a download link.
user0809 over 2 years ago
Could you be more precise on your need ? Where does the files come from ? a webserver ? a directory on your computer ? The best would be to have a demo page of your code working on firefox so we can adapt it to Safari
kerncy over 2 years ago
The files are dynamically created in the user's browser and do not come from anywhere. Here is a link to the very last bit of my current script, the part of the script which actually performs the file zipping and saving using FileSaver.js https://jsfiddle.net/b36eruzg/
user0809 over 2 years ago

Crowdsource coding tasks.

3 Solutions


Example markup

<a id="link" href="#0">No download yet</a>

Vanilla JavaScript

var
    link
;

link = document.getElementById("link");
link.href = "data:application/octet-stream," + encodeURIComponent(final_output_array);
link.download = "example-file.csv";
link.target = "_blank";
link.textContent = "Download example-file.csv";

jQuery

$("#link")
    .attr("href", "data:application/octet-stream," + encodeURIComponent(final_output_array))
    .attr("download", "example-file.csv")
    .attr("target", "_blank")
    .text("Download example-file.csv");

You should be able to create a download link for any file type like this.

I noticed that Safari does not support the download attribute yet, so please also try this out, if you experience problems:

jQuery using Blob

var
    blob
;

blob = new Blob(final_output_array, { type: "application/octet-stream" });

$("#link")
    .attr("href", URL.createObjectURL(blob))
    .attr("download", "example-file.csv")
    .attr("target", "_blank")
    .text("Download example-file.csv");

You might need to change application/octet-stream to the correct MIME type of the file content, e.g. text/xml (or application/xml) for XML, application/zip for zip, text/csv (or application/csv) for CSV files.

Could you modify this so it's jQuery? Thanks!
user0809 over 2 years ago
Here you go. Try it out.
LayZee over 2 years ago
I ran this and it did download a file from Safari but it's just called 'Unknown' and not 'example-file.csv' Can you try this on Safari here? https://jsfiddle.net/b36eruzg/3/
user0809 over 2 years ago
Also, for the 'encodeURIComponent' method, can I pass in anything? e.g. strings, arrays, objects etc and they will be properly converted for download. Just like FileSaver.js?
user0809 over 2 years ago
I just tried again the updated script, still 'Unknown' file name. I'm on Mac OS Sierra using Safari 10.0.1
user0809 over 2 years ago
Now I'm getting 'TypeError: plugin.IsCommandKeyUndetectEnable is not a function. (In 'plugin.IsCommandKeyUndetectEnable()', 'plugin.IsCommandKeyUndetectEnable' is undefined)' Can you test with the jsfiddle and see if it works? If it works for you can you please send me the jsfiddle link? https://jsfiddle.net/b36eruzg/6/ Thanks!
user0809 over 2 years ago
First argument to new Blob(array, object) must be an array: https://jsfiddle.net/b36eruzg/7/
LayZee over 2 years ago
Now this latest fiddle doesn't download anything when I run it... Does it work for you?
user0809 over 2 years ago
I tried this on Firefox and it works but I need it to work for Safari
user0809 over 2 years ago
I'm sorry, I neither have a Mac or Safari. I gave it a try. If you can't get FileSaver to work with blob, then I'm afraid that I can't help you.
LayZee over 2 years ago

I don't have Safari too. can you try it yourself, please?

// bitcoin:1C5NZCMkjJTf8v7t41QwW9EeCjJcLEbf5s
function setupInlineDownload(linkElement, fileName, fileContent) {
    var blob = new Blob([unicodeStringToTypedArray(fileContent)],  // yep, an array in array
        {type: 'application/octet-binary'});

    linkElement
        .attr('download', fileName)  // not supported in Safari (upto version 10), sorry
        .attr('href', URL.createObjectURL(blob))
    ;
}

// string to uint array, thanks to AJ ONeal @coolaj86
// see https://coolaj86.com/articles/unicode-string-to-a-utf-8-typed-array-buffer-in-javascript/
function unicodeStringToTypedArray(s) {
    var escstr = encodeURIComponent(s);
    var binstr = escstr.replace(/%([0-9A-F]{2})/g, function(match, p1) {
        return String.fromCharCode('0x' + p1);
    });
    var ua = new Uint8Array(binstr.length);
    Array.prototype.forEach.call(binstr, function (ch, i) {
        ua[i] = ch.charCodeAt(0);
    });
    return ua;
}

e.g.

setupInlineDownload($('#myLink'), 'test.csv', 'I\'m,not,so,good,in,CSV');

Hey user856,

First I tried most solutions with javascript(if not all), and most of them don't work - the file is downloaded with unknown file name or opened in new tab in the Safari browser. So I don't think js will work for this. Also you have said that this is only a purely static html page with no server. Actually this is not correct, because you are using a web server which sends the html page to the user browser. So depending on what hosting you will put your application, your hosting may provide support for php, .net, nodejs etc.

For me the best way to do this is to use a little php file alongiside your html page.

Create a new file called "force-download.php" alongside your index.html. The contents of force-download.php are:

<?php
$mimeType = $_POST['mimeType'];
$fileName = $_POST['fileName'];
$fileContent = $_POST['fileContent'];


header('Content-type: ' . $mimeType);
header('content-Disposition: attachment; filename=' . $fileName);
header('Pragma: no-cache');
header('Expires: 0');

echo $fileContent;

For html you only need this:

<div id="download-file"></div>

And this is your javascript after the html:

<script>
    var final_output_array = 'some data in array format';
    var fileName = 'newFile.csv';
    var mimeType = 'text/csv';
    var serverURL = 'force-download.php';

    makeDownloadLink(fileName, final_output_array, mimeType, serverURL);



    function makeDownloadLink(fileName, fileContent, mimeType, serverURL) {
        var form = $('<form id="downloadForm" action="'+serverURL+'" method="POST" target="_blank"></form>');

        form.append('<input type="hidden" name="fileName" value="'+fileName+'">');
        form.append('<input type="hidden" name="fileContent" value="'+fileContent+'">');
        form.append('<input type="hidden" name="mimeType" value="'+mimeType+'">');

        form.append('<input type="submit" value="Download File">');

        $('#download-file').append(form);
    }
</script>
Will you choose a solution?
dimitar 2 years ago
View Timeline