Comment définir un en-tête pour une requête HTTP GET et déclencher un téléchargement de fichier?

Mise à jour 20140702:

  • La solution
  • Réponse détaillée en tant que blog

(Mais je marque l'une des autres réponses acceptées au lieu de la mienne, car elle m'a fait à mi-chemin, et pour récompenser l'effort)


Il semble que définir un en-tête de requête HTTP n'est pas possible grâce à des liens avec <a href="..."> , et ne peut être effectué qu'avec XMLHttpRequest .

Cependant, l'URL liée est un fichier qui doit être téléchargé (le navigateur ne doit pas naviguer vers son URL), et je ne suis pas sûr que cela puisse être fait en utilisant AJAX.

En outre, le fichier renvoyé est un fichier binaire, et AJAX n'est pas destiné à cela.

Comment pourrait-on envisager de déclencher un téléchargement de fichier avec une requête HTTP qui comporte un en-tête personnalisé?

Modifier: corriger le lien brisé

essayer

Html

 <!-- placeholder , `click` download , `.remove()` options , at js callback , following js --> <a>download</a> 

Js

  $(document).ready(function () { $.ajax({ // `url` url: '/echo/json/', type: "POST", dataType: 'json', // `file`, data-uri, base64 data: { json: JSON.stringify({ "file": "data:text/plain;base64,YWJj" }) }, // `custom header` headers: { "x-custom-header": 123 }, beforeSend: function (jqxhr) { console.log(this.headers); alert("custom headers" + JSON.stringify(this.headers)); }, success: function (data) { // `file download` $("a") .attr({ "href": data.file, "download": "file.txt" }) .html($("a").attr("download")) .get(0).click(); console.log(JSON.parse(JSON.stringify(data))); }, error: function (jqxhr, textStatus, errorThrown) { console.log(textStatus, errorThrown) } }); }); 

Jsfiddle http://jsfiddle.net/guest271314/SJYy3/

Il existe deux façons de télécharger un fichier dans lequel la requête HTTP requiert la définition d' un en-tête .

Le crédit pour le premier va à @ guest271314, et le crédit pour le second passe à @dandavis.

La première méthode consiste à utiliser l'API de fichiers HTML5 pour créer un fichier local temporaire et la seconde à utiliser l'encodage base64 en conjonction avec un URI de données.

La solution que j'ai utilisée dans mon projet utilise l'approche de codage base64 pour les petits fichiers, ou lorsque l'API du fichier n'est pas disponible, sinon l'approche API du fichier.

Solution:

  var id = 123; var req = ic.ajax.raw({ type: 'GET', url: '/api/dowloads/'+id, beforeSend: function (request) { request.setRequestHeader('token', 'token for '+id); }, processData: false }); var maxSizeForBase64 = 1048576; //1024 * 1024 req.then( function resolve(result) { var str = result.response; var anchor = $('.vcard-hyperlink'); var windowUrl = window.URL || window.webkitURL; if (str.length > maxSizeForBase64 && typeof windowUrl.createObjectURL === 'function') { var blob = new Blob([result.response], { type: 'text/bin' }); var url = windowUrl.createObjectURL(blob); anchor.prop('href', url); anchor.prop('download', id+'.bin'); anchor.get(0).click(); windowUrl.revokeObjectURL(url); } else { //use base64 encoding when less than set limit or file API is not available anchor.attr({ href: 'data:text/plain;base64,'+FormatUtils.utf8toBase64(result.response), download: id+'.bin', }); anchor.get(0).click(); } }.bind(this), function reject(err) { console.log(err); } ); 

Notez que je n'utilise pas un XMLHttpRequest en cours et utilise plutôt ic-ajax , et devrait être assez similaire à une solution jQuery.ajax .

Notez également que vous devez remplacer text/bin et .bin par tout ce qui correspond au type de fichier en cours de téléchargement.

La mise en œuvre de FormatUtils.utf8toBase64 se trouve ici

J'ajoute une autre option. Les réponses ci-dessus m'ont été très utiles, mais je voulais utiliser jQuery au lieu d'ic-ajax (il semble avoir une dépendance avec Ember lorsque j'ai essayé d'installer à travers bower). Gardez à l'esprit que cette solution ne fonctionne que sur les navigateurs modernes.

Pour l'implémenter sur jQuery, j'ai utilisé jQuery BinaryTransport . C'est un bon plugin pour lire les réponses AJAX au format binaire.

Ensuite, vous pouvez le faire pour télécharger le fichier et envoyer les en-têtes:

 $.ajax({ url: url, type: 'GET', dataType: 'binary', headers: headers, processData: false, success: function(blob) { var windowUrl = window.URL || window.webkitURL; var url = windowUrl.createObjectURL(blob); anchor.prop('href', url); anchor.prop('download', fileName); anchor.get(0).click(); windowUrl.revokeObjectURL(url); } }); 

Les vars dans le script ci-dessus signifient:

  • Url: l'URL du fichier
  • En-têtes: un objet Javascript avec les en-têtes à envoyer
  • FileName: le nom de fichier que l'utilisateur verra lors du téléchargement du fichier
  • Ancrage: c'est un élément DOM qui est nécessaire pour simuler le téléchargement qui doit être enveloppé avec jQuery dans ce cas. Par exemple $('a.download-link') .

Je souhaite publier ici ma solution qui a été effectuée AngularJS, ASP.NET MVC. Le code illustre comment télécharger le fichier avec l'authentification.

Méthode WebApi avec classe auxiliaire:

 [RoutePrefix("filess")] class FileController: ApiController { [HttpGet] [Route("download-file")] [Authorize(Roles = "admin")] public HttpResponseMessage DownloadDocument([FromUri] int fileId) { var file = "someFile.docx"// asking storage service to get file path with id return Request.ReturnFile(file); } } static class DownloadFIleFromServerHelper { public static HttpResponseMessage ReturnFile(this HttpRequestMessage request, string file) { var result = request.CreateResponse(HttpStatusCode.OK); result.Content = new StreamContent(new FileStream(file, FileMode.Open, FileAccess.Read)); result.Content.Headers.Add("x-filename", Path.GetFileName(file)); // letters of header names will be lowercased anyway in JS. result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = Path.GetFileName(file) }; return result; } } 

Les modifications du fichier Web.config permettent d'envoyer le nom du fichier dans l'en-tête personnalisé.

 <configuration> <system.webServer> <httpProtocol> <customHeaders> <add name="Access-Control-Allow-Methods" value="POST,GET,PUT,PATCH,DELETE,OPTIONS" /> <add name="Access-Control-Allow-Headers" value="Authorization,Content-Type,x-filename" /> <add name="Access-Control-Expose-Headers" value="Authorization,Content-Type,x-filename" /> <add name="Access-Control-Allow-Origin" value="*" /> 

Pièce de service angulaire JS:

 function proposalService($http, $cookies, config, FileSaver) { return { downloadDocument: downloadDocument }; function downloadFile(documentId, errorCallback) { $http({ url: config.apiUrl + "files/download-file?documentId=" + documentId, method: "GET", headers: { "Content-type": "application/json; charset=utf-8", "Authorization": "Bearer " + $cookies.get("api_key") }, responseType: "arraybuffer" }) .success( function(data, status, headers) { var filename = headers()['x-filename']; var blob = new Blob([data], { type: "application/octet-binary" }); FileSaver.saveAs(blob, filename); }) .error(function(data, status) { console.log("Request failed with status: " + status); errorCallback(data, status); }); }; }; 

Dépendance de module pour FileUpload: angular-file-download (gulp install angular-file-download –save). L'inscription se ressemble ci-dessous.

 var app = angular.module('cool', [ ... require('angular-file-saver'), ]) . // other staff. 

Remarque: Cette réponse ne traite pas du paramètre d'un en-tête personnalisé, en raison d'une lecture erronée; Cependant, il existe plusieurs méthodes pour déclencher le téléchargement de fichiers. Merci à @bguiz et @Vivek pour les commentaires.

1) dans jQuery

 $('a.some-random-link').click(function(e) { e.preventDefault(); //stop the browser from following window.location.href = 'uploads/crazy-fractals.jpg'; }); 

2) en html avec l'attribut de download , mon préféré :

 <a href="/path/to/file.extension" download>download</a> 

VÉRIFIEZ CANIUSE.COM POUR LA COMPATIBILITÉ

3) js, j'ai aussi vu de temps en temps:

 function downloadFileFromLocation(fileName, url) { var dl = document.createElement('a'); dl.download = fileName; dl.href = url; dl.click(); }