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

How to get the filename from a file downloaded using Javascript Fetch API?

在我的 Javascript 客户端中,我使用 Fetch API 调用服务器来检索 server-generated 文件。我正在使用以下 client-side 代码:

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

这实际上工作得很好。但是,服务器会为该文件生成一个 filename 并将其作为 content-disposition header.

的一部分包含在响应中

我需要使用服务器生成的 filename 将这个文件保存到用户的机器上。在 Postman 中,我实际上可以看到响应的 content-disposition header 设置为:Content-Disposition: attachment;filename=myfilename.txt.

我尝试从响应中读取 content-disposition(请参阅我的 JS 代码中的警报),但我总是得到 null(即使相同的响应显示 content-disposition 在邮递员中)。

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

P.S。这是我的 server-side 返回文件的代码:

控制器操作

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

FileResult Class

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

编辑

我的问题是关于获取而不是 ajax api。此外,在我的代码中,我表明我已经在阅读响应中的 header,这与建议答案中显示的已接受答案完全一样。但是,正如我在 post 中所述,此解决方案不适用于 fetch。

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

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

这成功了!

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

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

决定 post 这个,因为接受的答案(虽然对很多人有帮助)实际上并没有回答关于如何做的原始问题:

"to get the filename from a file downloaded using javascript fetch api?".

可以读取文件名(如下图),下载文件的方法与this (the recommended downloadjs library by this does not get updated anymore; hence, I wouldn't suggest using it). The below also takes into account scenarios where the filename includes unicode characters (i.e.,-, !, (, ), etc.) and hence, comes (utf-8 encoded) in the form of, for instance, filename*=utf-8''Na%C3%AFve%20file.txt (see here for more details). In such cases, the decodeURIComponent()函数类似,用于解码filename。工作示例如下:

const url ='http://127.0.0.1:8000/'
fetch(url)
    .then(res => {
        const disposition = res.headers.get('Content-Disposition');
        filename = disposition.split(/;(.+)/)[1].split(/=(.+)/)[1];
        if (filename.toLowerCase().startsWith("utf-8''"))
            filename = decodeURIComponent(filename.replace("utf-8''", ''));
        else
            filename = filename.replace(/['"]/g, '');
        return res.blob();
    })
    .then(blob => {
        var url = window.URL.createObjectURL(blob);
        var a = document.createElement('a');
        a.href = url;
        a.download = filename;
        document.body.appendChild(a); // append the element to the dom, otherwise it won't work in Firefox
        a.click();
        a.remove(); // afterwards, remove the element  
    });

如已接受的答案中所述,如果您正在执行 cross-domain 请求,请确保将 Access-Control-Expose-Headers:Content-Disposition 添加到服务器端的响应 headers(以公开Content-Disposition header),否则 filename 将无法在客户端访问。例如:

headers = {'Access-Control-Expose-Headers': 'Content-Disposition'}
return FileResponse("Naïve file.txt", filename="Naïve file.txt", headers=headers)