web-dev-qa-db-fra.com

Téléchargement de fichier rapide avec XMLHttpRequest

Je suis conscient que la méthode ajax de jQuery ne peut pas gérer les téléchargements, et je ne souhaite pas ajouter de plug-in jQuery pour le faire.

Je veux savoir comment envoyer POST données avec XMLHttpRequest pour télécharger un fichier.

Voici ce que j'ai essayé:

var postData = new FormData();
postData.append('cells', JSON.stringify(output));

var xhr = new XMLHttpRequest();
xhr.open('POST', '/export/', true);
xhr.setRequestHeader("X-CSRFToken", csrftoken);
xhr.responseType = 'arraybuffer';
xhr.onload = function (e) {
    console.log(e);
    console.log(xhr);
}
xhr.send(postData);

Je travaille avec Django et le fichier semble bien être renvoyé au client. Dans l'onglet Réseau de Chrome, je peux voir du charabia dans l'onglet de prévisualisation (ce que j'attends). Mais je veux renvoyer un fichier Zip, pas une représentation textuelle du fichier Zip. Voici le back-end de Django:

wrapper = FileWrapper(tmp_file)
response = HttpResponse(wrapper, content_type='application/Zip')
response['Content-Disposition'] = "attachment; filename=export.Zip"
response['Content-Length'] = tmp_file.tell()
return response

J'ai recherché cela pendant des heures sans trouver un exemple approprié sur la façon de procéder avec XMLHttpRequests. Je ne souhaite pas créer un formulaire HTML approprié avec une action POST, car les données du formulaire sont plutôt volumineuses et créées de manière dynamique.

Y at-il quelque chose de mal avec le code ci-dessus? Quelque chose me manque? Je ne sais tout simplement pas comment envoyer les données au client en téléchargement.

12
bozdoz

La demande XHR ne déclenchera pas le téléchargement du fichier. Je ne trouve pas d'exigence explicite, mais la documentation du W3C sur XMLHttpRequest ne décrit aucune réaction particulière sur les réponses content-disposition = attachment

Vous pouvez télécharger le fichier par window.open () dans un onglet séparé, s’il ne s’agissait pas d’une demande POST. Ici il a été suggéré d'utiliser une forme cachée avec target = _blank

UPD: cette réponse n’est plus exacte depuis l’introduction de Blob API . Veuillez vous reporter à la réponse de Steven pour plus de détails.

10
Marat

Si vous définissez la propriété XMLHttpRequest.responseType sur 'blob' avant d'envoyer la demande, celle-ci sera alors représentée sous forme de blob lorsque vous recevrez la réponse. Vous pouvez ensuite enregistrer le blob dans un fichier temporaire et y accéder.

var postData = new FormData();
postData.append('cells', JSON.stringify(output));

var xhr = new XMLHttpRequest();
xhr.open('POST', '/export/', true);
xhr.setRequestHeader('X-CSRFToken', csrftoken);
xhr.responseType = 'blob';
xhr.onload = function (this, event) {
    var blob = this.response;
    var contentDispo = this.getResponseHeader('Content-Disposition');
    // https://stackoverflow.com/a/23054920/
    var fileName = contentDispo.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/)[1];
    saveOrOpenBlob(blob, fileName);
}
xhr.send(postData);

Et voici un exemple d'implémentation de saveOrOpenBlob:

function saveOrOpenBlob(blob, fileName) {
    window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
    window.requestFileSystem(window.TEMPORARY, 1024 * 1024, function (fs) {
        fs.root.getFile(fileName, { create: true }, function (fileEntry) {
            fileEntry.createWriter(function (fileWriter) {
                fileWriter.addEventListener("writeend", function () {
                    window.location = fileEntry.toURL();
                }, false);
                fileWriter.write(blob, "_blank");
            }, function () { });
        }, function () { });
    }, function () { });
}

Si vous ne souhaitez pas que le navigateur accède au fichier lorsqu'il s'agit d'un type de fichier visualisable, créer une méthode qui enregistre toujours directement dans un fichier est beaucoup plus simple:

function saveBlob(blob, fileName) {
    var a = document.createElement('a');
    a.href = window.URL.createObjectURL(blob);
    a.download = fileName;
    a.dispatchEvent(new MouseEvent('click'));
}
18
Steven Doggart

download: function(){
    var postData = new FormData();
		var xhr = new XMLHttpRequest();
		xhr.open('GET', downloadUrl, true);
		xhr.responseType = 'blob';
		xhr.onload = function (e) {
			var blob = xhr.response;
			this.saveOrOpenBlob(blob);
		}.bind(this)
		xhr.send(postData);
 }

saveOrOpenBlob: function(blob) {
		var assetRecord = this.getAssetRecord();
		var fileName = 'Test.mp4'
		var tempEl = document.createElement("a");
    	document.body.appendChild(tempEl);
    	tempEl.style = "display: none";
        url = window.URL.createObjectURL(blob);
        tempEl.href = url;
        tempEl.download = fileName;
        tempEl.click();
		window.URL.revokeObjectURL(url);
	},

Essayez ceci cela fonctionne pour moi.

0
Kamlesh Kumar