我通过在线教程和示例构建了一个基于jQuery客户端和MS WebAPI服务器的文件下载系统。
由于API需要身份验证,因此无法直接提供文件链接,因此文件URL是API终点而不是文件位置。
在服务器上,我有以下内容:
[HttpGet]
[Route("download/{filename}")]
public HttpResponseMessage DownloadFile(string filename)
{
try
{
// https://gist.github.com/joeriks/3714093
string path = string.Format("{0}/Exports/{1}", root, filename);
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
var stream = new FileStream(path, FileMode.Open);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = "download.txt";
return result;
}
catch (Exception ex)
{
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
}
返回预期的响应:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 4809
Content-Type: application/octet-stream
Expires: -1
Server: Microsoft-IIS/10.0
Content-Disposition: attachment; filename=download.txt
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, PUT, DELETE, GET, OPTIONS
Access-Control-Allow-Headers: content-Type, accept, origin, X-Requested-With, X-Authentication, X-Nonce, name
Date: Thu, 25 Oct 2018 13:07:25 GMT
在响应中有文本内容。到目前为止,一切都如我所希望的。
在客户端上,我有以下代码来处理从API返回的响应:
// https://dev59.com/e2Qo5IYBdhLWcg3wZepg#23797348
let disposition = jqXHR.getResponseHeader('Content-Disposition');
if (disposition && disposition.indexOf('attachment') !== -1) {
let filename = "scada-download.txt";
let matches = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(disposition);
if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
let type = jqXHR.getResponseHeader('Content-Type');
let blob = new Blob([data], { type: "text/csv" });
var downloadUrl = URL.createObjectURL(blob);
let $a = $("<a id='temp_download_link' style='display: none;' />").attr("href", downloadUrl).attr("download", filename);
$("body").append($a);
$a.trigger("click");
}
这个功能的确如广告所说,它会在页面上添加一个锚点并点击它以触发下载。
下载可以正常工作,并保存具有正确内容的文件。
唯一不起作用的部分是,在我测试过的两个浏览器中(Chrome 69、FF: 62),提供的默认文件名只是一个GUID。
目前,api和客户端代码都运行在我的本地开发机上,
http://127.0.0.1:9000/[client | api]
,因此跨域应该不会造成任何问题。通过ajax调用api。最终使用jQuery $.ajax()方法。
为了清晰起见,插入到DOM中的锚点是:
<a id="temp_download_link" style="display: none;" href="blob:http://127.0.0.1:9000/c2c5ffb5-3f22-4a57-8775-4e0bbfbfef9e" download="download.txt"></a>
Chrome默认提供的文件名是URL中的GUID,而FF则生成一个看似无关的6个字符的随机字符串。 具体地,为什么浏览器都忽略锚点的download="download.txt"属性和Content-Disposition: attachment; filename=download.txt? 更新: 我forked了这个fiddle:
添加了一些其他值到href属性中,似乎整个设置文件名的过程最好不要轻易改动:
更新2
我将一个可行示例的链接复制到了我的fiddle中,它可以从原始网站工作,但不能从fiddle中工作。
我有一种感觉,答案归结为“为什么它在David Walsh博客上有效,但在fiddle上无效?”
filename="download.txt"
吗?同时,清除了缓存(真的,完全清除了整个缓存)吗? - Alex