如何从使用 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)
在我的 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)