web-dev-qa-db-fra.com

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

Mise à jour 20140702:

(mais je marque l'une des autres réponses comme acceptée au lieu de la mienne, car cela m'a permis de gagner à mi-chemin et de récompenser l'effort)


Il semble que la définition d'un en-tête de requête HTTP n'est pas possible via des liens avec <a href="...">, et ne peut être fait qu'avec XMLHttpRequest.

Cependant, l'URL associée est un fichier qui devrait être téléchargé (le navigateur ne devrait pas accéder à son URL), et je ne suis pas sûr que cela puisse être fait avec AJAX.

En outre, le fichier renvoyé est un fichier binaire et AJAX n'est pas prévu pour cela.

Comment peut-on déclencher un téléchargement de fichier avec une requête HTTP à laquelle est ajouté un en-tête personnalisé?

edit: réparer le lien cassé

51
bguiz

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/

13
guest271314

Il y a deux manières de télécharger un fichier où la requête HTTP nécessite que un en-tête soit défini.

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

La première méthode consiste à utiliser l'API de fichier HTML5 pour créer un fichier local temporaire et la seconde consiste à utiliser le codage base64 conjointement à 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 de fichier n'est pas disponible, sinon utilisez l'approche de l'API de 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 brut, mais plutôt ic-ajax , et devrait être assez similaire à un jQuery.ajax Solution.

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

L'implémentation de FormatUtils.utf8toBase64peut être trouvé ici

45
bguiz

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

Afin d'implémenter cela sur jQuery, j'ai utilisé jQuery BinaryTransport . Ceci est un plugin de Nice pour lire les réponses AJAX au format binaire.

Ensuite, vous pouvez 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
  • nomfichier: le nom de fichier que l'utilisateur verra lors du téléchargement du fichier
  • anchor: c'est un élément du DOM nécessaire pour simuler le téléchargement qui doit être encapsulé avec jQuery dans ce cas. Par exemple, $('a.download-link').
12
dgaviola

je veux poster ma solution ici qui a été faite AngularJS, ASP.NET MVC. Le code montre comment télécharger un fichier avec authentification.

Méthode WebApi avec la classe d'assistance:

[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;
    }
}

Modifications du fichier Web.config pour permettre l'envoi d'un nom de fichier dans un 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="*" />

Partie de service JS angulaire:

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: téléchargement de fichier angulaire (gulp install - téléchargement de fichier angulaire --save). L'inscription ressemble à celle ci-dessous.

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

JQuery pur.

$.ajax({
  type: "GET",
  url: "https://example.com/file",
  headers: {
    'Authorization': 'Bearer eyJraWQiFUDA.......TZxX1MGDGyg'
  },
  xhrFields: {
    responseType: 'blob'
  },
  success: function (blob) {
    var windowUrl = window.URL || window.webkitURL;
    var url = windowUrl.createObjectURL(blob);
    var anchor = document.createElement('a');
    anchor.href = url;
    anchor.download = 'filename.Zip';
    anchor.click();
    anchor.parentNode.removeChild(anchor);
    windowUrl.revokeObjectURL(url);
  },
  error: function (error) {
    console.log(error);
  }
});
0
Eugene Maysyuk