需要从使用 javascript fetch api 下载的文件中获取文件名吗?

RHa*_*ris 12 javascript fetch-api

在我的 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);
})     
Run Code Online (Sandbox Code Playgroud)

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

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

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

难道我做错了什么?有没有办法使用 fetch 响应来检索文件名?有没有更好的方法从服务器获取文件名和文件?

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

控制器动作

public IHttpActionResult GetFile(){
   return new FileResult("myfilename.txt","Hello World!");
}
Run Code Online (Sandbox Code Playgroud)

文件结果类

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);
Run Code Online (Sandbox Code Playgroud)

编辑

我的问题是专门关于 fetch 而不是 ajax api。此外,在我的代码中,我表明我已经从响应中读取标题,与建议答案中显示的已接受答案完全相同。但是,正如我在帖子中所述,此解决方案不适用于 fetch。

RHa*_*ris 22

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

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

这有效!


ygl*_*odt 16

您可以从内容处置标头中提取文件名,如下所示:

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);
});
Run Code Online (Sandbox Code Playgroud)

  • @AFract 第一个 Promise 向服务器发送 HTTP 请求并等待响应。在收到的响应中,仅读取 HTTP 头。如果您想读取 HHTP 正文,它可以让您做出决定(基于标头和响应状态)。如果是 - 那么您将使用什么特定的阅读器(例如文本、json、blob)。第二个承诺是阅读正文。 (2认同)

Chr*_*ris 14

决定发布此内容,因为接受的答案(尽管对很多人有帮助)实际上并没有回答原始问题:

\n
\n

“如何从使用 JavaScript Fetch API 下载的文件中获取文件名?”

\n
\n

人们可以阅读filename(如下例所示),并使用与此类似的方法下载文件(downloadjs该文章中推荐的库不再维护;因此,我不建议使用它)。下面还考虑了其​​中filename包含 unicode 字符(即-, !, (, )等)并因此以例如(有关更多详细信息,请参阅此处utf-8)的形式出现(编码)的情况。在这种情况下,该函数用于解码.filename*=utf-8\'\'Na%C3%AFve%20file.txtdecodeURIComponent()filename

\n

工作示例

\n
const url =\'http://127.0.0.1:8000/\'\nfetch(url)\n    .then(res => {\n        const disposition = res.headers.get(\'Content-Disposition\');\n        filename = disposition.split(/;(.+)/)[1].split(/=(.+)/)[1];\n        if (filename.toLowerCase().startsWith("utf-8\'\'"))\n            filename = decodeURIComponent(filename.replace(/utf-8\'\'/i, \'\'));\n        else\n            filename = filename.replace(/[\'"]/g, \'\');\n        return res.blob();\n    })\n    .then(blob => {\n        var url = window.URL.createObjectURL(blob);\n        var a = document.createElement(\'a\');\n        a.href = url;\n        a.download = filename;\n        document.body.appendChild(a); // append the element to the dom\n        a.click();\n        a.remove(); // afterwards, remove the element  \n    });\n
Run Code Online (Sandbox Code Playgroud)\n

如果您正在进行跨源请求,请确保在服务器端设置Access-Control-Expose-Headers响应标头,以便公开Content-Disposition标头;否则,filename将无法通过 JavaScript 在客户端访问(请参阅此处的更多文档)。例如(摘自这个答案):

\n
headers = {\'Access-Control-Expose-Headers\': \'Content-Disposition\'}\nreturn FileResponse("Na\xc3\xafve file.txt", filename="Na\xc3\xafve file.txt", headers=headers)\n
Run Code Online (Sandbox Code Playgroud)\n