一、问题
在云存储项目中,由于使用 VFS 系统,文件 Blob 在物理硬盘(或对象存储)内的格式为 /objects/<uuid>。
浏览器在下载时,默认提取 Path 最后一段作为文件名(即文件名为:<uuid>)
二、解决方案
1. 若 CDN 支持动态改写响应 Header:
市面上其实大部分 CDN 都支持,但是部分厂商不会在控制台展示这个功能。
有的厂商需要提交工单配置(如:金山云 CDN);
有的厂商需要自行配置边缘函数规则(如:阿里云 ESA、腾讯云 EdgeOne)。
建议先看控制台有没有、再看是否支持边缘函数、最后提交工单询问、真搞不来也可以问问我(qq 2810132369)
在 CDN 响应头中动态新增 Header:
Content-Disposition: attachment; filename={动态插入请求URL中filename参数的值}
(如果你不需要访问 CDN 链接时强制下载,请将 attachment 改为 inline)注:如果你的 CDN 源站为私有对象存储 Bucket,请务必在 CDN 配置:
当未命中缓存时,在回源时不携带 filename 参数。
如果不配置,那么回源会失败(报错:SignatureDoesNotMatch)
2. 若 CDN 不支持动态改写响应 Header:
也是有办法的。
- 若你的前端域名和 CDN 域名同源:
(比如前端地址是https://user.edgecos.com,CDN 地址是https://user.edgecos.com/cdn,此时前端域名和 CDN 域名同源)
可利用 a 标签的 download 属性:
document.getElementById("downloadBtn").onclick = function () {
const url = ""; // 文件 CDN 地址
const filename = "自定义文件名.pdf"; // 查询 VFS 数据表,得到的真实文件名
const a = document.createElement("a");
a.href = url;
a.download = filename;
a.style.display = "none";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};(注意:这个方案的稳定性不太好,只能在前端域名和 CDN 域名同源时使用。并且并不是所有浏览器都支持该方案)
- 若你的服务器带宽比较大:
可以用服务器后端代理下载文件。但是这至少需要几百 Mbps 甚至数 Gbps 的公网上行带宽,并且会大量占用服务器资源。我想你应该不会愿意使用这个方案。
- 若你能接受下载文件的文件名固定为一个值:
可在 CDN 响应头添加静态 Header(所有 CDN 服务商一定都支持该功能)
Content-Disposition: attachment; filename=edgecos_download
(如果你不需要访问 CDN 链接时强制下载,请将 `attachment` 改为 `inline`)
你可以将 `edgecos_download` 改为你喜欢的文件名。此方案使得所有下载的文件名不再是 <uuid>,而是 edgecos_download。不过,我认为你应该不会喜欢这个方案。能用就尽量用 二、1 的方案