【问题标题】:How to set a header for a HTTP GET request, and trigger file download?如何为 HTTP GET 请求设置标头并触发文件下载?
【发布时间】:2014-08-21 11:42:14
【问题描述】:

更新 20140702:

(但我将其他答案之一标记为已接受,而不是我自己的, 因为它让我走到了一半,并奖励努力)


似乎无法通过<a href="..."> 的链接设置HTTP 请求标头,只能使用XMLHttpRequest 完成。

但是,链接到的 URL 是一个应该下载的文件(浏览器不应导航到其 URL),我不确定这是否可以使用 AJAX 完成。

另外,返回的文件是二进制文件,AJAX 不适用于此。

如何使用添加了自定义标头的 HTTP 请求触发文件下载?

编辑:修复断开的链接

【问题讨论】:

  • 为什么需要自定义请求标头?您必须代理此服务器端。
  • 该标头是由服务器设置的,而不是由 JS 设置的。但是,您可以轻松地 ajax 二进制文件(旧 IE 除外)并在 js 中初始化 blob/string 的下载。
  • @dandavis 我需要从客户端设置一个自定义标头(包含令牌) - 即客户端向服务器发出的 HTTP GET 请求需要包含标头,否则服务器将简单地返回错误代码。
  • xhr 因为 v2 已经支持 xhr.responseType = 'blob';您使用 url=window.URL.createObjectURL(xhr.response) 将 blob 转换为 url,将锚的 href 设置为该 url,将锚的下载属性设置为(ex)'thefilename.ext',然后调用锚.click() 将文件移动到客户端的下载文件夹。然后你可以调用 anchor.remove() 来清理。
  • 如果您有文件链接,为什么不直接使用download 属性?

标签: javascript ajax http-headers download


【解决方案1】:

试试

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/

【讨论】:

  • @guest271314 感谢您的回答,这适用于较小的文件,但不适用于较大的文件(> 1mb)。我将您的解决方案标记为已接受,因为它帮助很大。我的actual solution can be found here
【解决方案2】:

纯 jQuery。

$.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);
  }
});

【讨论】:

    【解决方案3】:

    我想在这里发布我的解决方案,它是由 AngularJS、ASP.NET MVC 完成的。该代码说明了如何通过身份验证下载文件。

    WebApi 方法和辅助类:

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

    Web.config 文件更改以允许在自定义标头中发送文件名。

    <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="*" />
    

    Angular 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);
        });
    };
    };
    

    FileUpload 的模块依赖项:angular-file-download (gulp install angular-file-download --save)。注册如下所示。

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

    【讨论】:

      【解决方案4】:

      有两种方法可以下载文件,其中 HTTP 请求要求设置标头

      第一个功劳归@​​guest271314,第二个功劳归@​​dandavis。

      第一种方法是使用 HTML5 File API 创建一个临时的本地文件, 第二种是将base64编码与数据URI结合使用。

      我在项目中使用的解决方案对小文件使用了base64编码方式, 或者当文件 API 不可用时, 否则使用 File API 方法。

      解决方案:

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

      请注意,我没有使用原始的XMLHttpRequest, 而是使用ic-ajax, 并且应该与jQuery.ajax 解决方案非常相似。

      另请注意,您应该将text/bin.bin 替换为与正在下载的文件类型对应的任何内容。

      FormatUtils.utf8toBase64的实现 can be found here

      【讨论】:

      • 另外值得一提的是您在出色的blog post 中提出的观点,即通过创建一个临时下载路径来实现此服务器端,该路径在服务器接收到具有适当的常规请求后不需要标头包括标题。
      • 现在看起来像404,我最近发布时一定打破了一些别名。这是更新的链接:blog.bguiz.com/2014/07/02/…
      • 看起来你又移动了它,这是链接:blog.bguiz.com/2014/07/03/…。感谢您的详细回答!
      • 可以了,还是不是很开心,因为文件是先静默下载的,下载时不会出现在下载中。
      • 你能看到这个链接吗stackoverflow.com/questions/48189365/…
      【解决方案5】:

      我正在添加另一个选项。上面的答案对我来说非常有用,但我想使用 jQuery 而不是 ic-ajax(当我尝试通过 bower 安装时,它似乎与 Ember 有依赖关系)。请记住,此解决方案仅适用于现代浏览器。

      为了在 jQuery 上实现这一点,我使用了jQuery BinaryTransport。这是一个不错的插件,可以读取二进制格式的 AJAX 响应。

      然后你可以这样做来下载文件并发送标题:

      $.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);
          }
      });
      

      上述脚本中的变量表示:

      • url:文件的URL
      • headers:带有要发送的标头的 Javascript 对象
      • fileName:用户下载文件时看到的文件名
      • anchor:它是模拟下载所需的 DOM 元素,在这种情况下必须用 jQuery 包装。例如$('a.download-link')

      【讨论】:

      • 非常干净简单的解决方案! +1
      • 简单明了。这可能会有所帮助 var anchor = $("");锚.css(“显示”,“无”); $("body").append(anchor); anchor.prop('href', url); anchor.prop('下载', 文件名);锚点.get(0).click(); windowUrl.revokeObjectURL(url);锚点.remove();
      猜你喜欢
      • 2014-08-23
      • 2012-10-03
      • 1970-01-01
      • 1970-01-01
      • 2013-04-12
      • 2012-08-06
      • 1970-01-01
      • 1970-01-01
      • 2011-06-24
      相关资源
      最近更新 更多