Search code examples
javascriptjqueryajaxhttpzip

Recieving a Zip file as response on AJAX request


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?


Solution

  • 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 <henryalgus@gmail.com>
     *
     */
    
    // 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