web-dev-qa-db-de.com

Client-Download einer vom Server generierten ZIP-Datei

Bevor jemand sagt, "duplizieren", möchte ich nur sicherstellen, dass die Leute wissen, dass ich diese Fragen bereits gelesen habe:

1) Verwendet eckig und php, nicht sicher, was hier passiert (ich kenne PHP nicht): Zip-Datei herunterladen und Dialog "Datei speichern" aus der Winkelmethode auslösen

2) Kann diese Antwort nicht erhalten, um irgendetwas zu tun: So laden Sie eine ZIP-Datei mithilfe von angle

3) Diese Person kann bereits heruntergeladen werden, was hinter dem Punkt liegt, den ich zu ermitteln versuche: Externe Zip-Datei von Winkel herunterladen, die auf einer Schaltfläche ausgelöst wird

4) Keine Antwort auf diese Frage: .ZIP-Datei vom Server in nodejs herunterladen

5) Ich weiß nicht, welche Sprache das überhaupt ist: https://stackoverflow.com/questions/35596764/Zip-file-download-using-angularjs-directive

Angesichts dieser Fragen , wenn dies noch ein Duplikat ist, entschuldige ich mich. Hier ist noch eine andere Version dieser Frage.

Mein eckiger 1.5.X-Client gibt mir eine Liste von Titeln, von denen jeder eine zugeordnete Datei hat. Mein Node 4.X/Express 4.X-Server übernimmt diese Liste, ruft die Dateispeicherorte ab, erstellt mithilfe von express-Zip von npm eine ZIP-Datei und streamt diese Datei anschließend in der Antwort zurück. Ich möchte, dass mein Client die Option "Datei herunterladen" des Browsers initiiert. 

Hier ist mein Client-Code (Angular 1.5.X):

function bulkdownload(titles){
    titles = titles || [];
    if ( titles.length > 0 ) {
        $http.get('/query/bulkdownload',{
            params:{titles:titles},
            responseType:'arraybuffer'
        })
        .then(successCb,errorCb)
        .catch(exceptionCb);
    }

    function successCb(response){
        // This is the part I believe I cannot get to work, my code snippet is below
    };

    function errorCb(error){
            alert('Error: ' + JSON.stringify(error));
    };

    function exceptionCb(ex){
            alert('Exception: ' + JSON.stringify(ex));
    };
};

Node (4.X) -Code mit Express-Zip, https://www.npmjs.com/package/express-Zip :

router.get('/bulkdownload',function(req,resp){
    var titles = req.query.titles || [];

    if ( titles.length > 0 ){
        utils.getFileLocations(titles).
        then(function(files){
            let filename = 'zipfile.Zip';

            // .Zip sets Content-Type and Content-disposition
            resp.Zip(files,filename,console.log);
        },
        _errorCb)
    }
});

Hier ist mein successCb in meinem Client-Code (Angular 1.5.X):

function successCb(response){
    var URL = $window.URL || $window.webkitURL || $window.mozURL || $window.msURL;
    if ( URL ) {
        var blob = new Blob([response.data],{type:'application/Zip'});
        var url = URL.createObjectURL(blob);
        $window.open(url);
    }
};

Der "Blob" -Teil scheint gut zu funktionieren. Bei der Überprüfung im IE-Debugger sieht es aus wie ein Datenstrom von Oktett-Informationen. Nun, ich glaube, ich muss diesen Klecks in die HTML5-Direktive aufnehmen, um das "Save File As" vom Browser aus zu initiieren. Könnte sein? Vielleicht nicht?

Da mehr als 90% unserer Benutzer IE11 verwenden, teste ich alle meine Winkel in PhantomJS (Karma) und IE. Beim Ausführen des Codes erhalte ich in einem Warnfenster den alten Fehler "Zugriff wird verweigert":

Exception: {"description":"Access is denied...<stack trace>}

Anregungen, Klarstellungen, Antworten usw. sind willkommen!

5
westandy

Ich habe meine bulkdownload-Methode aktualisiert, um $window.open(...) anstelle von $http.get(...) zu verwenden:

function bulkdownload(titles){
    titles = titles || [];
    if ( titles.length > 0 ) {
        var url = '/query/bulkdownload?';
        var len = titles.length;
        for ( var ii = 0; ii < len; ii++ ) {
            url = url + 'titles=' + titles[ii];
            if ( ii < len-1 ) {
                url = url + '&';
            }
        }
        $window.open(url);
    }
};

Ich habe das nur in IE11 getestet. 

1
westandy
var Zip_file_path = "" //put inside "" your path with file.Zip
var Zip_file_name = "" //put inside "" file name or something
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
a.href = Zip_file_path;
a.download = Zip_file_name;
a.click();
document.body.removeChild(a);
4
atom

Verwende das hier:

var url="YOUR Zip URL HERE";
window.open(url, '_blank');
1
Chinmay235

Wie in diese Antwort angegeben, habe ich die unten stehende Javascript-Funktion verwendet und kann nun den byte[] array-Inhalt erfolgreich herunterladen.

Funktion zum Konvertieren eines Byte-Array-Streams (String-Typ) in ein Blob-Objekt:

var b64toBlob = function(b64Data, contentType, sliceSize) {
      contentType = contentType || '';
      sliceSize = sliceSize || 512;

      var byteCharacters = atob(b64Data);
      var byteArrays = [];

      for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
        var slice = byteCharacters.slice(offset, offset + sliceSize);

        var byteNumbers = new Array(slice.length);
        for (var i = 0; i < slice.length; i++) {
          byteNumbers[i] = slice.charCodeAt(i);
        }

        var byteArray = new Uint8Array(byteNumbers);

        byteArrays.Push(byteArray);
      }

      var blob = new Blob(byteArrays, {type: contentType});
      return blob;
};

Und so rufe ich diese Funktion auf und speichere das Blob-Objekt mit FileSaver.js (Daten über Angular.js $http.get abrufen):

    $http.get("your/api/uri").success(function(data, status, headers, config) {
        //Here, data is type of string
        var blob = b64toBlob(data, 'application/Zip');
        var fileName = "download.Zip";
        saveAs(blob, fileName);
    });

Hinweis: Ich sende den byte[] array (Java-Server-Side) wie folgt:

byte[] myByteArray = /*generate your Zip file and convert into byte array*/ new byte[]();
return new ResponseEntity<byte[]>(myByteArray , headers, HttpStatus.OK);
0