问题描述
Chrome's Blob Storage System Design 文档提到了以下内容。
如果 blob 的内存空间已满,或者新 blob 太大而无法在内存中,则 blob 系统使用磁盘。这可以是将旧的 blob 分页到磁盘,也可以将过大的新 blob 直接保存到磁盘。
然而,即使在多次阅读文档后,我仍然有以下担忧:
- 我仍然不确定使用 fetch 在哪里影响这种行为并首先将数据加载到内存中。
- 如果 fetch 实际上改变了这种行为,是否有推荐用于此方法的文件大小限制(并且不应下载超过该大小的任何文件)?
- 在其他(非基于铬的浏览器)中会出现什么行为?
const download = downloadLinks => {
const _download = async ( downloadLink ) => {
const blobURL = await fetch(downloadLink,{
responseType: 'blob'
})
.then(res => res.blob())
.then(blob => window.URL.createObjectURL(blob))
const fileName = downloadLink.substr(downloadLink.lastIndexOf('/'))
const a = document.createElement('a')
a.href = blobURL
a.setAttribute('download',fileName)
document.body.appendChild(a)
a.click()
a.remove()
window.URL.revokeObjectURL(blobURL)
}
const downloadInterval = () => {
if (downloadLinks.length == 0) return
const url = downloadLinks.pop()
_download(url)
if (downloadLinks.length !== 0) setTimeout(downloadInterval,500)
}
setTimeout(downloadInterval,0)
}
以下是我浏览的一些资源。这些回答了所有三个问题的一部分,但我有点担心如果 Blob 首先加载到内存中,fetch 可能会产生什么影响。
解决方法
简短的回答是肯定的!
-
Fetch 实际上改变了这种行为,因为它首先将数据作为
ReadableStream
加载到内存中。因此,请改用以下代码。 -
此方法可以下载的最大文件大小取决于磁盘大小、操作系统和浏览器。没有适用于所有系统的确切数字。此问题已详细回答here。
-
“没有明显的硬性限制。我能够创建比 FileSaver.js 声称的“800 MiB”大得多的 Blob。它不使用磁盘空间来支持更大的 Blob,因此它可能全部进入内存操作系统将内存分页到磁盘。这意味着可能会出现大于内存的 blob,但性能可能会很差。"
点击 2 和 here 中的链接了解更多详情。
正如@kaiido 在评论中提到的以及我通过运行一些测试也发现的一些内容,如果您期望一个大文件以及如何利用 Blob 架构(如果可能的话,直接在磁盘中加载文件)上述内容代码可以修改如下。
const download = downloadLinks => {
const _download = url => {
const xhr = new XMLHttpClient()
xhr.responseType = 'blob'
xhr.open('GET',url)
xhr.onload = () => {
const fileName = url.substr(url.lastIndexOf('/'))
const blobURL = window.URL.createObjectURL(xhr.response)
const a = document.createElement('a')
a.href = blobURL
a.setAttribute('download',fileName)
document.body.appendChild(a)
a.click()
a.remove()
window.URL.revokeObjectURL(blobURL)
}
xhr.send(null)
}
const downloadInterval = () => {
if (downloadLinks.length == 0) return
const url = downloadLinks.pop()
_download(url)
if (downloadLinks.length !== 0) setTimeout(downloadInterval,500)
}
setTimeout(downloadInterval,0)
}
此处的区别在于 xhr.responseType = 'blob'
行。尽管在原始问题中可以看出我们的请求对象有一个 responseType
选项,但它不起作用,因为 fetch API 一开始就没有该选项。