【问题标题】:How to get the filename from a file downloaded using Javascript Fetch API?需要从使用 javascript fetch api 下载的文件中获取文件名?
【发布时间】:2018-08-23 11:35:35
【问题描述】:

在我的 javascript 客户端中,我使用 fetch 调用服务器来检索服务器生成的文件。我正在使用以下客户端代码:

var _url = "";    
var initParms = {  
   method: "GET",
   mode: 'cors'
}

fetch(_url, initParms)
.then(response => {
   if(response.ok){
      alert(response.headers.get("content-disposition"));
      return response.blob();
   }

   throw new Error("Network response was not OK.");
})
.then(blob => {
   var url = new URL.createObjectURL(blob);
})     

这实际上工作得很好。但是,服务器会为该文件生成一个文件名,并将其作为 content-disposition 标头的一部分包含在响应中。

我需要使用服务器生成的文件名将此文件保存到用户的机器上。在 Postman 中,我实际上可以看到响应的内容处置标头设置为:Content-Disposition: attachment;filename=myfilename.txt

我尝试从响应中读取内容处置(请参阅我的 js 代码中的警报),但我总是得到 null(即使相同的响应显示 Postman 中的内容处置)。

我做错了吗?有没有办法使用 fetch 响应来检索文件名?有没有更好的方法从服务器获取文件名以及文件?

附:这是我返回文件的服务器端代码:

控制器动作

public IHttpActionResult GetFile(){
   return new FileResult("myfilename.txt","Hello World!");
}

文件结果类

public class FileResult : IHttpActionResult
{
   private string _fileText = "";
   private string _fileName = "";
   private string _contentType = "";

   public FileResult(string name, string text)
   {
       _fileText = text;
       _fileName = name;
       _contentType = "text/plain";
   }

   public Task<HttpResponseMessage> ExecuteActionAsync(CancellationToken token)
   {
        Stream _stream = null;
        if (_contentType == "text/plain")
        {
            var bytes = Encoding.Unicode.GetBytes(_fileText);
            _stream = new MemoryStream(bytes);
        }
        return Task.Run(() =>
        {
            var response = new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StreamContent(_stream),
            };

            response.Content.Headers.ContentType = 
                new MediaTypeHeaderValue(_contentType);
            response.Content.Headers.ContentDisposition = 
                new ContentDispositionHeaderValue("attachment")
            {
                FileName = _fileName
            };

            return response;

        }, token);

编辑

我的问题是关于 fetch 而不是 ajax api。此外,在我的代码中,我表明我已经在从响应中读取标题,就像在建议的答案中展示的接受的答案一样。但是,正如我在帖子中所述,此解决方案不适用于 fetch。

【问题讨论】:

标签: javascript fetch-api


【解决方案1】:

您可以像这样从 content-disposition 标头中提取文件名:

let filename = '';

fetch(`/url`, { headers: { Authorization: `Bearer ${token}` }}).then((res) => {
    const header = res.headers.get('Content-Disposition');
    const parts = header!.split(';');
    filename = parts[1].split('=')[1];
    return res.blob();
}).then((blob) => {
    // Use `filename` here e.g. with file-saver:
    // saveAs(blob, filename);
});

【讨论】:

  • 它对我有用。但是(作为 JS 的非专家),我想知道:为什么后面的两个“Then”承诺,第一个带有响应,第二个带有 blob,什么时候在单个“Then”中处理所有内容可能会更容易?谢谢
  • @AFract 第一个 Promise 向服务器发送 HTTP 请求并等待响应。在收到的响应中,仅读取 HTTP 头。如果您想读取 HHTP 正文,它可以让您做出决定(基于标头和响应状态)。如果是,那么您将使用什么特定的阅读器(例如文本、json、blob)。第二个承诺是阅读正文。
  • @iTollu 我明白了,谢谢。我假设,因为我知道 API 以及为什么调用它,我没有这样的疑惑,我只是想获取我的内容;)
  • @AFract 我假设,由于 Fetch API 是一个内置的浏览器工具,它是故意更底层的。让自定义实现使其适应特定的用例。例如,在我参与的项目中,我们编写了一个简单的包装函数,该函数进行响应状态检查、一些错误处理并读取正文。正如您所说,此函数向调用代码返回一个承诺。 W3C 很可能决定让开发人员编写这样的自定义函数比在 API 中涵盖所有可能的情况更容易。但也许 MDN 的某个人可以对此发表评论 - 太好了!
【解决方案2】:

所以,在发布这个问题后不久,我遇到了this issue on Github。它显然与使用 CORS 有关。

建议的解决方法是将Access-Control-Expose-Headers:Content-Disposition 添加到服务器上的响应标头中。

成功了!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-03
    • 2019-03-27
    • 2020-07-05
    • 2018-05-30
    • 2018-05-04
    • 2017-10-25
    • 1970-01-01
    相关资源
    最近更新 更多