如何将大文件直接下载到磁盘,而不将其存储在服务器和浏览器的 RAM 中?
How to download a big file directly to the disk, without storing it in RAM of a server and browser?
我想使用 Node.js 和 [= 从运行我的应用程序的同一台服务器(没有外部云文件存储,也就是本地)实现大文件下载(大约 10-1024 Mb) 63=].
我想出了如何将整个文件转换为 Blob
,通过网络传输它,然后生成下载 link 和 window.URL.createObjectURL(…)
的 Blob
。只要文件很小,这种方法就非常有效,否则不可能将整个 Blob
保存在服务器和客户端的 RAM 中。
我尝试使用 File API 和 AJAX 实现其他几种方法,但它看起来 Chrome 将整个文件加载到 RAM 中,然后才将其转储到磁盘。同样,小文件可能没问题,但大文件就不行了。
我最后一次尝试发送一个基本的 Get
-请求:
const aTag = document.createElement("a");
aTag.href = `/downloadDocument?fileUUID=${fileName}`;
aTag.download = fileName;
aTag.click();
在服务器端:
app.mjs
app.get("/downloadDocument", async (req, res) => {
req.headers.range = "bytes=0";
const [urlPrefix, fileUUID] = req.url.split("/downloadDocument?fileUUID=");
const downloadResult = await StorageDriver.fileDownload(fileUUID, req, res);
});
StorageDriver.mjs
export const fileDownload = async function fileDownload(fileUUID, req, res) {
//e.g. C:\Users\User\Projects\POC\assets\wanted_file.pdf
const assetsPath = _resolveAbsoluteAssetsPath(fileUUID);
const options = {
dotfiles: "deny",
headers: {
"Content-Disposition": "form-data; name=\"files\"",
"Content-Type": "application/pdf",
"x-sent": true,
"x-timestamp": Date.now()
}
};
res.sendFile(assetsPath, options, (err) => {
if (err) {
console.log(err);
} else {
console.log("Sent");
}
});
};
当我单击 link 时,Chrome 会在“下载”中显示该文件,但状态为 失败 - 无文件。下载目标中没有文件。
我的问题:
- 为什么在发送
Get
请求时我得到 失败 - 没有文件?
- 据我了解,
res.sendFile
对于小文件来说是一个正确的选择,但是对于大文件来说,最好使用res.write
,它可以被分割成块。是否可以将 res.write
与 Get
-请求一起使用?
P.S。我已经详细阐述了这个问题,以使其更加狭窄和清晰。之前这个问题的重点是从 Dropbox 下载一个大文件而不将其存储在 RAM 中,可以找到答案:
Chrome无法显示下载进度,因为文件正在后台下载。下载后,将创建文件的 link 并“单击”以强制 Chrome 显示已下载文件的对话框。
可以更轻松地完成。您需要创建一个 GET
请求并让浏览器下载文件,而无需 ajax.
app.get("/download", async (req, res, next) => {
const { fileName } = req.query;
const downloadResult = await StorageDriver.fileDownload(fileName);
res.set('Content-Type', 'application/pdf');
res.send(downloadResult.fileBinary);
});
function fileDownload(fileName) {
const a = document.createElement("a");
a.href = `/download?fileName=${fileName}`;
a.download = fileName;
a.click();
}
我想使用 Node.js 和 [= 从运行我的应用程序的同一台服务器(没有外部云文件存储,也就是本地)实现大文件下载(大约 10-1024 Mb) 63=].
我想出了如何将整个文件转换为 Blob
,通过网络传输它,然后生成下载 link 和 window.URL.createObjectURL(…)
的 Blob
。只要文件很小,这种方法就非常有效,否则不可能将整个 Blob
保存在服务器和客户端的 RAM 中。
我尝试使用 File API 和 AJAX 实现其他几种方法,但它看起来 Chrome 将整个文件加载到 RAM 中,然后才将其转储到磁盘。同样,小文件可能没问题,但大文件就不行了。
我最后一次尝试发送一个基本的 Get
-请求:
const aTag = document.createElement("a");
aTag.href = `/downloadDocument?fileUUID=${fileName}`;
aTag.download = fileName;
aTag.click();
在服务器端:
app.mjs
app.get("/downloadDocument", async (req, res) => {
req.headers.range = "bytes=0";
const [urlPrefix, fileUUID] = req.url.split("/downloadDocument?fileUUID=");
const downloadResult = await StorageDriver.fileDownload(fileUUID, req, res);
});
StorageDriver.mjs
export const fileDownload = async function fileDownload(fileUUID, req, res) {
//e.g. C:\Users\User\Projects\POC\assets\wanted_file.pdf
const assetsPath = _resolveAbsoluteAssetsPath(fileUUID);
const options = {
dotfiles: "deny",
headers: {
"Content-Disposition": "form-data; name=\"files\"",
"Content-Type": "application/pdf",
"x-sent": true,
"x-timestamp": Date.now()
}
};
res.sendFile(assetsPath, options, (err) => {
if (err) {
console.log(err);
} else {
console.log("Sent");
}
});
};
当我单击 link 时,Chrome 会在“下载”中显示该文件,但状态为 失败 - 无文件。下载目标中没有文件。
我的问题:
- 为什么在发送
Get
请求时我得到 失败 - 没有文件? - 据我了解,
res.sendFile
对于小文件来说是一个正确的选择,但是对于大文件来说,最好使用res.write
,它可以被分割成块。是否可以将res.write
与Get
-请求一起使用?
P.S。我已经详细阐述了这个问题,以使其更加狭窄和清晰。之前这个问题的重点是从 Dropbox 下载一个大文件而不将其存储在 RAM 中,可以找到答案:
Chrome无法显示下载进度,因为文件正在后台下载。下载后,将创建文件的 link 并“单击”以强制 Chrome 显示已下载文件的对话框。
可以更轻松地完成。您需要创建一个 GET
请求并让浏览器下载文件,而无需 ajax.
app.get("/download", async (req, res, next) => {
const { fileName } = req.query;
const downloadResult = await StorageDriver.fileDownload(fileName);
res.set('Content-Type', 'application/pdf');
res.send(downloadResult.fileBinary);
});
function fileDownload(fileName) {
const a = document.createElement("a");
a.href = `/download?fileName=${fileName}`;
a.download = fileName;
a.click();
}