saveAs: file-saver/download/fetch/blob 文件下载原理
下载文件的一个不错的包,解析一下下载文件的原理
安装
yarn add file-saver
公司项目的请求
- 这个请求,是由后端生成的
- 生成过程,有2个点
- 添加 attachement 头
curl 'https://kellis-ng-alo7-com.oss-cn-beijing.aliyuncs.com/images/c23f54de640de8dfe5f2247e7d38f733.png?Expires=1756194185&OSSAccessKeyId=LTAI5tSfywws519stBvtNg6R&Signature=1kw7Vsjb12nprXNynnj0f0mhkiI%3D&response-content-disposition=attachment%3Bfilename%3Dc23f54de640de8dfe5f2247e7d38f733.png' \
-X 'HEAD' \
-H 'Accept: */*' \
-H 'Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,ru;q=0.6' \
-H 'Connection: keep-alive' \
-H 'Origin: https://kellis-ng.alo7.com' \
-H 'Referer: https://kellis-ng.alo7.com/' \
-H 'Sec-Fetch-Dest: empty' \
-H 'Sec-Fetch-Mode: cors' \
-H 'Sec-Fetch-Site: cross-site' \
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36' \
-H 'company-source: apply7' \
-H 'sec-ch-ua: "Not;A=Brand";v="99", "Google Chrome";v="139", "Chromium";v="139"' \
-H 'sec-ch-ua-mobile: ?0' \
-H 'sec-ch-ua-platform: "macOS"'
生成预签名
import oss2
# 初始化 bucket
auth = oss2.Auth('your-access-key-id', 'your-access-key-secret')
bucket = oss2.Bucket(auth, 'https://oss-cn-beijing.aliyuncs.com', 'your-bucket-name')
# 生成预签名 URL,强制下载,并指定文件名
url = bucket.sign_url(
method='GET',
key='object-in-oss.pdf', # OSS 中的实际文件名
expires=3600, # 过期时间(秒)
params={
'response-content-disposition': 'attachment; filename="custom-filename.pdf"'
}
)
print(url)
file-saver 实现
从原理上说明,为什么这种场景 file-saver 走不通。
- 不同域名 - 发送 head 请求 → 这里,由于签名会关心方法,所以HEAD 走不通
- 然后动态创建 a 标签
- 添加 download 属性 - 并添加文件名
- 添加 href 链接
- 添加 norefer 等特定标签
最终的方案
- 有可能有一个标记的过程(下载次数)
window.open(responseData.url, '_blank');
项目中的代码
- 这个不要使用
window.fetch(responseData.url
,因为这个下载文件到内存流 - 而 window.open 是直接
网络IO → 本地的 FileIO
简化过程,也没有内存问题
import { message } from 'antd';
// import { saveAs } from 'file-saver';
// import { resouseDownloadHelper } from 'common/help/resouseDownloadHelper';
export function download(url: string, fileName: string, subject: string, uuid: string) {
alo7Api
.fetch(alo7Api.prefix.host + `/api/v1/resources_${subject}/${uuid}/download`, {
method: 'PUT',
})
.then(responseData => {
// saveAs(responseData.url, fileName);
window.open(responseData.url, '_blank');
// window.fetch(responseData.url, { cache: 'no-cache' }).then(res => {
// const { url } = res;
// resouseDownloadHelper(url, fileName);
// });
})
.catch(error => message.error('下载失败:' + error.message));
}
stream 问题
有时候,后端返回是
application/octet-stream
就得走 fetch + blob 方式了
HTTP/1.1 200 OK
Server: AliyunOSS
Date: Wed, 27 Aug 2025 08:40:40 GMT
Content-Type: application/octet-stream
Content-Length: 813953
Connection: close
x-oss-request-id: 68AEC48801B31832319E64AF
Accept-Ranges: bytes
ETag: "710EE1CEE9D3B301A0DE9252486E0A13"
Last-Modified: Tue, 03 Dec 2024 02:44:34 GMT
x-oss-object-type: Normal
x-oss-hash-crc64ecma: 7405199409781227046
x-oss-storage-class: Standard
Content-MD5: cQ7hzunTswGg3pJSSG4KEw==
x-oss-server-time: 37