Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

saving Zip file problems #156

Closed
jvsteiner opened this issue Jul 2, 2015 · 32 comments
Closed

saving Zip file problems #156

jvsteiner opened this issue Jul 2, 2015 · 32 comments

Comments

@jvsteiner
Copy link

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?

@jvsteiner
Copy link
Author

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

@crowmagnumb
Copy link

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.

@jvsteiner
Copy link
Author

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.

@crowmagnumb
Copy link

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. :(

@jimgeiger
Copy link

@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

@crowmagnumb
Copy link

@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!

@liadmagen
Copy link

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

@divyakumbukkal
Copy link

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

@divyakumbukkal
Copy link

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

        });

@divyakumbukkal
Copy link

@crowmagnumb it worked with your suggestion of using XMLHttpRequest

@eligrey
Copy link
Owner

eligrey commented May 13, 2016

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

@evilaliv3
Copy link
Contributor

related to #219

@eligrey
Copy link
Owner

eligrey commented May 21, 2016

@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.

@eligrey
Copy link
Owner

eligrey commented May 21, 2016

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.

@eligrey eligrey closed this as completed May 21, 2016
@chaideos
Copy link

@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

@Nkunzis
Copy link

Nkunzis commented May 31, 2017

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.

@divyakumbukkal
Copy link

use jquery, it worked for me.

@Nkunzis
Copy link

Nkunzis commented May 31, 2017

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

@divyakumbukkal
Copy link

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

@Nkunzis
Copy link

Nkunzis commented May 31, 2017

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.

@divyakumbukkal
Copy link

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

@Nkunzis
Copy link

Nkunzis commented May 31, 2017

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

@divyakumbukkal
Copy link

so its clear that backend was not providing zip properly

@Nkunzis
Copy link

Nkunzis commented May 31, 2017

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

@jimmywarting
Copy link
Collaborator

jimmywarting commented May 31, 2017

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

@ekaitzht
Copy link

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

@jimmywarting
Copy link
Collaborator

jimmywarting commented Feb 19, 2018

@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 #156 (comment) 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

@Perry5
Copy link

Perry5 commented Feb 22, 2018

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

@eric-burel
Copy link

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.

@serv
Copy link

serv commented Oct 1, 2018

For those who are using jszip on the server side, refer to this thread as well
Stuk/jszip#276

It was helpful in solving my problem.

@anilkumartm
Copy link

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

@pettired
Copy link

pettired commented Oct 1, 2020

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests