如何将大文件直接下载到磁盘,而不将其存储在服务器和浏览器的 RAM 中?

Mik*_* B. 7 javascript data-transfer download chunking node.js

我想从我的应用程序使用 Node.js 和 Express.js 运行的同一服务器(没有外部云文件存储,也就是本地)实现一个大文件下载(大约 10-1024 Mb)。

我想通了如何将整个文件转换为做到这一点Blob,通过网络传输,然后生成一个下载链接window.URL.createObjectURL(…)Blob。只要文件很小,这种方法就可以完美地工作,否则不可能将整个文件保存在Blob服务器或客户端的 RAM 中。

我尝试使用File APIAJAX实现其他几种方法,但看起来 Chrome 将整个文件加载到 RAM 中,然后才将其转储到磁盘。同样,对于小文件可能没问题,但对于大文件则不是一种选择。

我的最后一次尝试是发送一个基本的请求Get

const aTag = document.createElement("a");
aTag.href = `/downloadDocument?fileUUID=${fileName}`;
aTag.download = fileName;
aTag.click();
Run Code Online (Sandbox Code Playgroud)

在服务器端:

应用程序.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);

});
Run Code Online (Sandbox Code Playgroud)

存储驱动程序

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");
        }

    });

};
Run Code Online (Sandbox Code Playgroud)

当我点击链接时,Chrome 会在下载中显示该文件,但状态为Failed - No file。下载目的地中没有文件出现。

我的问题:

  1. 为什么在发送Get-request 的情况下我会失败 - 没有文件
  2. 据我了解,res.sendFile对于小文件来说,可能是一个正确的选择,但对于大文件,最好使用res.write,它可以分成块。是否可以res.writeGet-request一起使用?

PS我已经详细说明了这个问题,以使其更加狭窄和清晰。以前这个问题主要是从 Dropbox 下载一个大文件而不将它存储在 RAM 中,可以找到答案: 如何使用 Node.js 从 Dropbox 下载一个大文件?

art*_*nik 3

Chrome 无法有效显示下载进度,因为文件正在后台下载。下载完成后,会创建一个指向该文件的链接,并“单击”以提示 Chrome 显示已下载文件的对话框。

这可以更容易地完成。您需要创建一个GET请求并让浏览器下载文件。

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);
});
Run Code Online (Sandbox Code Playgroud)
function fileDownload(fileName) {
  const a = document.createElement("a");
  a.href = `/download?fileName=${fileName}`;
  a.download = fileName;
  a.click();
}
Run Code Online (Sandbox Code Playgroud)

  • [这是一个示例](https://codesandbox.io/s/shy-leftpad-ydubq?file=/src/index.js:0-1100),其中“sendFile”位于“res.download”下。由于绝对路径,第一个链接不起作用,但下一个链接很好。我已经更改了“选项”中的“根”。 (2认同)
  • 我没有主意了。您可以在“codesandbox”上隔离您的问题吗?只是具有相同逻辑的下载部分。也许我可以帮助你调试这部分。 (2认同)
  • 看来和我是平等的。它们都基于底层的“stream”。[`res.write`](https://nodejs.org/api/http.html#http_response_write_chunk_encoding_callback),根据文档,可以“流式传输”文件,以及 `res.sendFile` 使用这个名为 [` 的包send`](https://www.npmjs.com/package/send) 是基于 [`fs.createReadStream`](https://nodejs.org/api/fs.html#fs_fs_createreadstream_path_options) ([源代码](https://github.com/pillarjs/send/blob/master/index.js))。但我不能百分百确定,也许这些流在某些方面有所不同。 (2认同)
  • 我在 GitHub 上发现了一些问题,但这些问题与正文大小_(换句话说上传)_有关,https://github.com/expressjs/express/issues/4237 /sf/ 13374238/如何在express-js中限制上传文件大小 (2认同)