How to download a big file (10-50 Mb) from Dropbox with Node.js?

£可爱£侵袭症+ 提交于 2020-07-22 04:35:05

问题


I want to implement a big file downloading (approx. 10-50 Mb). I've already succeeded to get a file from Dropbox:

operationResult = await dbx.filesDownload({
    path: `/${CONFIG_STORAGE.uploader.assetsPath}/${fileUUID}`
});

Then I bundle the received file with a meta-data and I return it to my Node.js server:

fileMIME = mime.lookup(operationResult.name);

const downloadResult = Object.freeze({
    fileBinary: operationResult.fileBinary,
    fileLength: operationResult.fileBinary.length,
    fileMIME,
    fileName: operationResult.name,
    isSucceeded,
    message
});

return downloadResult;

Now I convert a Buffer, I got from Dropbox, into a Readable stream and pipe it back to a client:

res.setHeader("Content-Disposition", "attachment; filename=" + downloadResult.fileName);
res.setHeader("Content-Type", downloadResult.fileMIME);

const fileReadableStream = new Readable();

fileReadableStream.push(downloadResult.fileBinary);
fileReadableStream.push(null);

fileReadableStream.pipe(res);

Up until now everything is clear and works. Here I face a first pitfall: I need somehow to trigger a download process in browser.

In many examples, some small image or JSON are used, which we can completely load into RAM, make operations, e.g. transforming to Base64, assign it to a.href, and trigger a.click(). But since my file is 10-50 Mb I'm not sure if such approach is a correct one.

I've already tried Fetch API:

const response = await fetch(`${host}/download?fileName=${fileName}`, {
    credentials: "same-origin",
    method: "POST",
    mode: "cors"
});

const a = document.createElement("a");
a.href = response.text();
a.download = "MyFile.pdf";
a.click();

But I always get Failed - No file error. I also tried to use jQuery AJAX, and XMLHttpRequest (XHR), but still no file is downloaded.

Perhaps, there is something I'm missing. How to get a 10-50 Mb file from a server?

P.S. I never thought that such a trivial task, as a file downloading, can be so complicated.


回答1:


I've solved the issue by switching from filesDownload to filesGetTemporaryLink, which returns a link to a file instead of the file itself. Then I trigger a downloading of this link.

The final result:

operationResult = await dbx.filesGetTemporaryLink({
    path: `/${CONFIG_STORAGE.uploader.assetsPath}/${fileUUID}`
});

const downloadResult = Object.freeze({
    fileLength: operationResult?.metadata.size,
    fileLink: operationResult?.link,
    fileMIME: mime.lookup(operationResult?.metadata.name),
    fileName: operationResult?.metadata.name,
    isSucceeded,
    message
});

return downloadResult;

Then I send the output to a client:

res.json(downloadResult);

On a client-side I get it via await/async Fetch API call:

const fileResponse = await fetch(``${host}/downloadDocument`, {
    body: JSON.stringify({fileUUID: fileName}),
    cache: "no-cache",
    credentials: "same-origin",
    headers: {
        "Content-Type": "application/json"
    },
    method: "POST",
    mode: "cors"
});

const fileData = await fileResponse.json();

const aTag = document.createElement("a");

aTag.href = fileData.fileLink;
aTag.download = fileData.fileName;
aTag.click();

As a result, a server doesn't have to deal with files at all, no extra CPU, RAM, or traffic impact, no matter what size of a file I'm trying to download.



来源:https://stackoverflow.com/questions/62740008/how-to-download-a-big-file-10-50-mb-from-dropbox-with-node-js

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!