How to download a zip file in AngularJS / Filesave

2019-03-06 08:21发布

This question already has an answer here:

i have a service function which return me 3 result files(2 text files, 1 zip file).

i want to check the header of the response to see if the file is a zip or a text to set the right configuration to download the file.

my controller resolved the promise from my service. if i download a text file -> the file says [object object] ..

i have seen in my browser that the response body is from content-type: text/plain

i am bit confused :/

controller :

$scope.getResult = function(rid) {
        console.log($scope.id);
        GetResultFile.getResult($scope.id, rid)
        .then(function (data, headers) {
            //console.log(headers(Content-type));
            console.log("Download erfolgreich");
            console.log(data.status);
            var file = new Blob([data], {type: 'text/plain;charset=utf-8'});
            FileSaver.saveAs(file, 'test.txt');

        }, function (data) {
            console.log("Download ERROR!");
            console.log(data.status);
        })
    };      

service :

.factory('GetResultFile',
['$http', '$q',
function ($http, $q) {

    var service = {};
    service.getResult = function(id, rid) {

        var deferred = $q.defer();

        $http
        .get('http://localhost:9999/v1/jmeter/' + id + '/results/' + rid, {cache: false})
        .then(function(data, status, headers, config) {
            if(data.status == 200) {
                console.log(data.status);
                deferred.resolve(data);
            }
            else {
                deferred.reject(data);
            }
        });
        return deferred.promise;                
    }
    return service;

view:

<span ng-show="dataTable">
    <table>
        <tr ng-repeat="r in results">
            <td><a href="" ng-click="getResult(r.id)" download>{{r.name}}</a></td>
        </tr>
    </table>
</span>

Update:

this is my service where i set the responsetype to arraybuffer

.factory('GetZip',
['$http', '$q',
function ($http, $q) {

    var service = {};
    service.getZip = function(id, rId) {

        var deferred = $q.defer();

        $http
        .get('http://localhost:9999/v1/jmeter/' + id + '/results/' + rId, {cache: false, responseType: 'arraybuffer'})
        .then(function(response) {
            if(response.status == 200) {
                console.log(response.status);
                deferred.resolve(response);
            }
            else {
                deferred.reject(response);
            }
        });
        return deferred.promise;                
    }
    return service;

this is my call in controller :

$scope.getResult = function(rId, rName) {
        console.log($scope.id);
        console.log(rName);
        GetResultFile.getResult($scope.id, rId)
        .then(function(response) {
            var data = "";
            var headerType = "";
            data = response.data;
            headerType = response.headers('content-type');
            console.log(response.headers('content-type'));
            console.log("Download erfolgreich");
            console.log(response);
            // zip data need to be handle with a binary stream
            if(headerType == 'application/zip;charset=UTF-8') {
                GetZip.getZip($scope.id, rId)
                .then(function(response) {
                    var file = new Blob([response.data], {type: "application/octet-stream"});
                    FileSaver.saveAs(file, rName);
                    console.log("zipper");
                }, function(response) {
                    console.log("Download ERROR");
                    console.log(response.status);
                })
            } else {
                var file = new Blob([data], {type: headerType});
                FileSaver.saveAs(file, rName);
                console.log("else");
            }
        }, function(response) {
            console.log("Download ERROR!");
            console.log(response.status);
        })
    };              

}])

but if i download the zip file, its still corrupt and a error while extract the zip -.-

2条回答
老娘就宠你
2楼-- · 2019-03-06 08:52

You should have 2 separate rest services on backend for this so instead of serving all files at the same time and holding them in browser, you should retrieve list of available files from one rest endpoint and then on user's click retrieve specific file from another rest endpoint. Your service for file could look something like this:

function downloadFile(src) {
  let header, startIndex, filename;
  $http({
    method: 'GET',
    url: src,
    headers: {
      'Accept': 'text/html,application/xhtml+xml,application/xml,application/pdf;q=0.9,image/webp,*/*;q=0.8',
      'Upgrade-Insecure-Requests': 1
    },
    responseType: 'blob'
  }).then(function (successResponse) {
    header = unescape(successResponse.headers()['content-disposition']);
    startIndex = header.indexOf('filename=');
    filename = header.slice(startIndex + 10,-1);
    try{
      saveAs(successResponse.data, filename);
    }
    catch(error){
      // FileSaver error
    }
  }, function (errorResponse) {
    // Server error has occurred during file downloading.
}

Where you need to specify response type to be blob. Variable src in this example would be specific endpoind serving desired file (here it's served from controller based on data retrieved from list of files available but you can build it here if you want based on file's id or something). In this case backend is serving full file name in exposed header so you can extract it and save file with it's proper name and format. Extraction in this example was for specific rest response but you should be able to change it to suite your needs.

Example: content disposition header would be string, something like:

Content-Disposition: attachment; filename="picture.png"

In this case service would extract that name and Filesaver would save file "picture.png".

查看更多
贪生不怕死
3楼-- · 2019-03-06 09:00

How to download a zip file in AngularJS

To download a binary file type such as a zip file, it is important to set the responseType before doing the XHR.

var config = { responseType: "blob" };

var promise = $http.get(url, config);

promise
  .then(function onSuccess(response) {
    // Handle success
    var data = response.data;
    var status = response.status;
    var statusText = response.statusText;
    var headers = response.headers;
    var config = response.config;
    ...
  }).catch(function onError(response) {
    // Handle error
    var data = response.data;
    var status = response.status;
    var statusText = response.statusText;
    var headers = response.headers;
    var config = response.config;
    ...
  });

For more information, see MDN Web API Reference - XHR responseType.

查看更多
登录 后发表回答