Filesaver.js: saving Zip file problems

Created on 2 Jul 2015  路  32Comments  路  Source: eligrey/FileSaver.js

Hi I have been using FileSaver.js with no problems to save binary data generated in browser, but am having problems with the following code:

console.log(myData.length); // yields 13292 - which is the correct data length
var blob = new Blob([myData], {type: "application/zip"});
console.log(blob.size); // yields 13404
saveAs(blob, "data.zip"); 

Note the difference in lengths. The result is that the zip file will not unpack correctly. myData is a string containing binary data. I have also tried reading it into an array first, various different mime types (none, text, octet-stream). Any ideas?

Most helpful comment

solved it with:

function str2bytes (str) {
    var bytes = new Uint8Array(str.length);
    for (var i=0; i<str.length; i++) {
        bytes[i] = str.charCodeAt(i);
    }
    return bytes;
}

usage:

var blob = new Blob([str2bytes(myData)], {type: "application/zip"});
saveAs(blob, "data.zip"); 

All 32 comments

solved it with:

function str2bytes (str) {
    var bytes = new Uint8Array(str.length);
    for (var i=0; i<str.length; i++) {
        bytes[i] = str.charCodeAt(i);
    }
    return bytes;
}

usage:

var blob = new Blob([str2bytes(myData)], {type: "application/zip"});
saveAs(blob, "data.zip"); 

I'm having a similar problem with this as outlined here ...

http://stackoverflow.com/questions/34756633/trouble-downloading-zip-file-with-spring-boot

... and your fix didn't help me. I think I'm doing something else wrong. Think it might be the way I'm streaming it from Spring but not sure.

not sure I can help here - in my case I was generating the zip file to be downloaded in the browser, not retrieving it from a server. And I don't do Java.

My problem turned out to have to do with using angular's $http service to make the call (jQuery's ajax method failed as well). But if I used the builtin XMLHttpRequest it worked just fine! No need to convert the byte array even.

var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = "blob";
xhr.withCredentials = true;
xhr.onreadystatechange = function (){
    if (xhr.readyState === 4) {
        var blob = xhr.response;
        FileSaver.saveAs(blob, filename);
    }
};
xhr.send();

No idea why those other packages returned the wrong data. :(

@crowmagnumb you might have better luck with the angular $http service in this context by setting responseType to be 'arraybuffer'. I had a similar problem with zip/kmz content from the server until I saw this post: http://stackoverflow.com/questions/26767160/download-a-zip-file-in-angularjs

@jimgeiger Good to know. I'm not going to go back now to verify as that code is working. But if I have to refactor for some reason I'll definitely give this a try. Cheers!

Hi,
Setting the _responseType_ to be '_arraybuffer_' was working for me. So I guess it is an acceptable solution for this. Thank you @jimgeiger.

My only problem with it is that I can not distinguish between a file download or an error from the server (i.e. file not found) as everything returns as an arraybuffer that way and there is no way to parse the response. except maybe setting a different HTTP statusCode ...

am also facing similar kind of issue, I tried giving responseType to be 'arraybuffer' , but didn't work. I could see the zip from which is from spring controller on the ui - IE Network - responsebody is showing the zip, but zip cannot be retrieved in angular

Here is my code:

             $http.get("getzipfiles.html",config,
                {
              responseType: "arraybuffer",
              cache: false,
              headers: {
                  'Content-Type': 'application/zip; charset=utf-8'
                }

            }).success(function(data, status, headers, config) {



          console.log("Trying saving zip ...");
          console.log(data.length); 
          var blob = new Blob([data], {type: 'application/zip'});
          console.log(blob.size); 
          var fileName = "out.zip";
          saveAs(blob, fileName);


          console.log("saveBlob succeeded");

        }).error(function(data, status) {

        });

@crowmagnumb it worked with your suggestion of using XMLHttpRequest

Seems like a problem with angular. Should I close this issue, or any of you not using angular and experiencing this?

@evilaliv3 He's calling console.log(blob.size); and experiencing the issue before calling saveAs(). FileSaver.js doesn't appear to be related to the issue.

If anyone finds any errors in FileSaver.js causing this issue, and not angular or the Blob API, please mention me in this issue and I will check it out.

@divyakumbukkal It seems like the angular $http and $resource modules expect the response to be an Object. In order to make sure that the binary file received is as an arraybuffer, 2 things to be done. I have modified your code and posted a code which I used using $resource service for your reference in case your modified code does not work

  1. Using the $http service to receive the response as arraybuffer
$http.get("getzipfiles.html",config,
                {
              responseType: "arraybuffer",
              cache: false,
              headers: {
                  'Content-Type': 'application/zip; charset=utf-8'
                },
             transformResponse: function (data, headers) {
                    //The data argument over here is arraybuffer but $http returns response
                   // as object, thus returning the response as an object with a property holding the
                   // binary file arraybuffer data
                    var response = {};
                    response.data = data;
                    return response;
                }
            }).success(function(response, status, headers, config) {



          console.log("Trying saving zip ...");
          console.log(response.data.length); 
          var blob = new Blob([response.data], {type: 'application/zip'});
          console.log(blob.size); 
          var fileName = "out.zip";
          saveAs(blob, fileName);


          console.log("saveBlob succeeded");

        }).error(function(data, status) {

        });

Here is my code snippet using the $resource

var restApi = $resource('some-url-for-zip-file', {}, {
            get: {
                headers: {
                    'Content-Type': 'application/zip',
                    'Accept': 'application/zip'
                },
                responseType: 'arraybuffer',
                transformResponse: function (data, headersGetter) {
                    var response = {};
                    response.data = data;
                    return response;
                }
            }
        });
//Use the $resource to make the REST call
 restApi.get().$promise
                .then(function downloadFile(response) {
                var data = new Blob([response.data], {
                    type: 'application/zip;charset=utf-8'
                });
                FileSaver.saveAs(data, 'logs.zip');
        }).catch(function requestFailed(error) {
            console.log(error);
        });

Let me know if it works for you

I found this StackOverflow post helpful for the problem http://stackoverflow.com/questions/35000245/getting-a-binary-file-from-an-angular-resource

Hi
I similar issue with zip files. I can see binary content in the response body. I've tried all solutions suggested above but none works.

use jquery, it worked for me.

@divyakumbukkal : i tried the solution with XMLHttpRequest but did not work

var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = "blob"; // changed this to arraybuffer but did not work as well
xhr.withCredentials = true;
xhr.onreadystatechange = function (){
    if (xhr.readyState === 4) {
        var blob = xhr.response;
        FileSaver.saveAs(blob, filename);
    }
};
xhr.send();

the same code worked for me, check in developer tools if you are getting zip file properly

my response header looks like this:

Cache-Control:no-cache, no-store, must-revalidate
Connection:Close
Content-Disposition:attachment; filename="server_logs.zip"
Content-Type:application/zip
Date:Wed, 31 May 2017 15:34:51 GMT
Expires:0
Pragma:no-cache

and i can see binaries in the body of the response. the zip file is also properly created locally, i create a local copy on hdd for testing and send stream of it as response data.

see if you can save zip file inside developer tools of firefox/chrome

i have "File not found" error in developer tools.
By the way Thanks for the help u r providing.

so its clear that backend was not providing zip properly

looks like that...but i can still see a zip file saved locally as backup by the server.

If you have Content-Disposition:attachment; filename="server_logs.zip" then the only thing you have to do is to open the link, you don't need FileSaver for this


PS, next time you post code block use 3 ` followed by the syntax: eg

` ``javascript
code
` ``

but without space

@eligrey I am having this issue but it's happening to me with AJAX, and axios inside react I have tried changing the enpoint that is generating the zip file and I am still have the problem.

I tried with blob with different types, etc.

   $.ajax({
            url: "http://localhost:80", 
            type: "GET",
            responseType: 'arraybuffer',
            processData: false,
            success: function (res) {
                var blob = new Blob([res], { type: 'application/octet-stream' });
                var fileName = "QCPReport.zip";
                saveAs(blob, fileName);
            }
        });

@ekaitzht jQuery ajax don't support arraybuffer or blob, you creating the blob the wrong way.

Read this on how to support it: http://keyangxiang.com/2017/09/01/HTML5-XHR-download-binary-content-as-Blob/

Frankly i think jQuery's ajax sucks. I prefer regular XMLHttpRequest over jQuery's but would definitely choose the fetch api if it's available to you

fetch('http://localhost')
  .then(res => res.blob())
  .then(blob => saveAs(blob, fileName))

or see https://github.com/eligrey/FileSaver.js/issues/156#issuecomment-172120287 for xhr

But hey. if the file is coming from the server i would not use the fileSaver lib at all. use content disposition attachment header to tell browser to trigger the download. It has grater cross browser support for all browsers and you won't have to buffer up the clients memory

[jvsteiner commented on Jul 2, 2015] - This solution worked perfectly for me. The code on my server side looked something like:

const csv1 = json2csv({ data: data1, fields: fields1 });
const csv2 = json2csv({ data: data2, fields: fields2});

zip.file('csvFile1.csv', csv1);
zip.file('csvFile2.csv', csv2);

const data = zip.generate({ base64:false, compression: 'DEFLATE' });

res.status(200).send(data);

Hi, just had similar issue using axios for fetching with a server-side generated file.

When fetching the zip file, you must ensure that it is not transformed into a string by the client lib, this can corrupt the data.

In axios (that rely on xhr), the responseType must be set to blob, as said by @crowmagnumb. When using fetch, res.blob() must be called to transform the result. Then saveAs(yourData, "myzip.zip") will work fine without creating a blob.

For those who are using jszip on the server side, refer to this thread as well
https://github.com/Stuk/jszip/issues/276

It was helpful in solving my problem.

@crowmagnumb - Great tip! XHR worked after trying 10-12 different approaches.. Was totally clueless. Thank you!

Hi Team,

I need to save a zip with 3 files of same name. Currently, the zip is storing only 1 file as 3 of the files have same name. Can you please help me here
Below is my code:

var zip = new jszip();
                        for (var i = 0; i < result.length; i++) {
                            var b64data = result[i];
                            zip.file(filesSelected[i].file_name, b64data, {base64: true});
                            if (i === result.length - 1) {
                                zip.generateAsync({type: "blob", compression: "DEFLATE"})
                                        .then(function (b64data) {
                                            saveAs(b64data, "Downloads.zip");
                                        })
                            }
                        }
Was this page helpful?
0 / 5 - 0 ratings