Recieving a Zip file as response on AJAX request

24,049

Solution 1

An approach utilizing XMLHttpRequest(); check if a element has download property, if true, set download property to an objectURL; else, use window.open() with parameter objectURL of Blob response

function downloadFile(url, headers, filename) {

  function handleFile(data) {
    console.log(this.response || data);
    var file = URL.createObjectURL(this.response || data);
    filename = filename || url.split("/").pop();
    var a = document.createElement("a");
    // if `a` element has `download` property
    if ("download" in a) {
      a.href = file;
      a.download = filename;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
    } else {
      // use `window.open()` if `download` not defined at `a` element
      window.open(file)
    }
  }

  var request = new XMLHttpRequest();
  request.responseType = "blob";
  request.onload = handleFile;
  request.open("GET", url);
  for (var prop in headers) {
    request.setRequestHeader(prop, headers[prop]);
  }

  request.send();
}

downloadFile("/path/to/resource/", {"x-content": "abc"}, "filename.zip")

jQuery version using fork of jquery-ajax-blob-arraybuffer.js

/**
 *
 * jquery.binarytransport.js
 *
 * @description. jQuery ajax transport for making binary data type requests.
 * @version 1.0 
 * @author Henry Algus <[email protected]>
 *
 */

// use this transport for "binary" data type
$.ajaxTransport("+binary", function(options, originalOptions, jqXHR){
    // check for conditions and support for blob / arraybuffer response type
    if (window.FormData && ((options.dataType && (options.dataType == 'binary')) 
        || (options.data 
        && ((window.ArrayBuffer && options.data instanceof ArrayBuffer) 
        || (window.Blob && options.data instanceof Blob))))
       )
    {
        return {
            // create new XMLHttpRequest
            send: function(headers, callback){
        // setup all variables
                var xhr = new XMLHttpRequest(),
        url = options.url,
        type = options.type,
        async = options.async || true,
        // blob or arraybuffer. Default is blob
        dataType = options.responseType || "blob",
        data = options.data || null,
        username = options.username || null,
        password = options.password || null;

                xhr.addEventListener('load', function(){
            var data = {};
            data[options.dataType] = xhr.response;
            // make callback and send data
            callback(xhr.status
                    , xhr.statusText
                    , data
                    , xhr.getAllResponseHeaders());
                });

                xhr.open(type, url, async, username, password);

        // setup custom headers
        for (var i in headers ) {
            xhr.setRequestHeader(i, headers[i] );
        }

                xhr.responseType = dataType;
                xhr.send(data);
            },
            abort: function(){
                jqXHR.abort();
            }
        };
    }
});
function downloadFile(url, headers, filename) {
return $.ajax({
  url:url,
  dataType:"binary",
  processData: false,
  headers:headers
})
.then(function handleFile(data) {
    console.log(this.response || data);
    var file = URL.createObjectURL(this.response || data);
    filename = filename || url.split("/").pop();
    var a = document.createElement("a");
    // if `a` element has `download` property
    if ("download" in a) {
      a.href = file;
      a.download = filename;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
    } else {
      // use `window.open()` if `download` not defined at `a` element
      window.open(file)
    }
  })
}

downloadFile("/path/to/resource/", {"x-custom-header":"abc"}, "filename.zip");

Just have to download it, that's all

You can use <a> element, download attribute

$("<a>", {href: someUrl,
        download: "filename.zip"
}).appendTo("body")[0].click()

Alternatively parse file using a library, e.g., zip.js, create multiple or single downloadable .zip from data contained within file.

Create an objectURL of each file, download each file using a element.

If download attribute is not available at browser, you can use data URI of file object with MIME type set to application/octet-stream to download file

Solution 2

I ended up using fetch api. For me its more clear whats goin on:

fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(dwnloadModel)
        })
            .then((response) => {
                    response.blob().then((blob) => {
                    const downloadUrl = window.URL.createObjectURL(blob);
                    const link = document.createElement('a');
                    link.setAttribute('href', downloadUrl);
                    link.setAttribute('download', 'file');
                    link.style.display = 'none';
                    document.body.appendChild(link);
                    link.click();
                    window.URL.revokeObjectURL(link.href);
                    document.body.removeChild(link);
                })
            });

I basicly take the blob from my response and put it to url, which i then append to dom as a 'a' tag and click it by js. Once is clicked i clean up the a tag and revoke the url. works fine for me.

Share:
24,049
Kevyn Quiros
Author by

Kevyn Quiros

Midnight Rock n' Roll blaster and pizza fueled code machine. Javascript and Ruby lover, musician, ocasional pyromaniac, and Internet procastinator. I actually think C++ jokes are the best but they work out better in spanish (My native language)!! Very excited about Fantastic Beasts and Where to Find Them!!!!!!

Updated on April 01, 2021

Comments

  • Kevyn Quiros
    Kevyn Quiros about 3 years

    So I'm working on a website that needs to make a call to the server and it returns a zip file, the thing is that I'm not entierly sure I'm doing everything right. The code looks kind of like this:

    function download(){
      if($('.download').hasClass('activeBtn')){
        $.ajax({
            type: 'GET',
            url: someUrl,
            contentType: 'application/zip',
            dataType: 'text',
            headers: {
                'Api-Version': '3.4'
            }
    
        }).then(function (data) {
          console.log(data); //Basically prints the byte array
          //Here I should build the file and download it
        });
      }
    }
    

    As you can see I need to makeup the file with the byte array that is in the response, how can I do that?