У нас есть приложение React/Node, размещенное в Digital Ocean. Мы также используем пространство Digital Ocean, которое совместимо с AWS S3 для хранения объектов. По сути, приложение представляет собой своего рода внутренний дропбокс. У нас есть администраторы, которые могут создавать папки и загружать контент в эти папки. Затем у нас есть клиенты, которые могут войти в систему и загрузить любые файлы, к которым мы разрешаем им доступ.
Нам удалось загрузить все файлы в Digital Ocean Spaces. Неважно, насколько они большие/маленькие.
Проблема заключается в том, что когда мы пытаемся загрузить (как администратор или клиент) любой контент размером более 100 МБ, мы сталкиваемся с ошибкой JavaScript Heap of memory. Эта ошибка появляется на бэкенде системы.
Вот некоторые решения, которые мы пытались администрировать:
Внешний код
downloadFile = (id, name, type) => {
axios
.get(
`/test-download/${id}`,
this.props.handleSnackBar(
"Your download has been started. Please wait."
)
)
.then(res => {
download(
new Blob([new Uint8Array(res.data.data.Body.data)]),
`${name}.${type}`
);
console.info(res);
console.info(res.data.data.Body),
this.props.handleSnackBar("Your download is now ready.");
})
.catch(err => console.info(err));
};
Внутренний код
app.get("/test-download/:id", (req, res) => {
var params = {
Bucket: bucketName,
Key: req.params.id
};
s3.getObject(params, function(err, data) {
//
console.info(data);
//
if (!err) {
res.send({ data, key: params.Key });
} else {
console.info({ err }); // an error occurred
}
});
});
Бэкэнд-код с потоком
app.get("/test-download/:id", (req, res) => {
var params = {
Bucket: bucketName,
Key: req.params.id
};
// TRY
const fileRequest = s3.getObject(params);
let chunks = [];
fileRequest
.createReadStream()
.on("data", function(data) {
console.info(`Received ${data.length} bytes of data`);
chunks.push(data);
})
.on("end", function() {
console.info("no more data");
bufferData = Buffer.concat(chunks);
console.info(bufferData);
res.send({ bufferData, key: params.Key });
});
});
В общем, я застрял. Любая помощь, которая может быть предложена, приветствуется. Спасибо.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Проблема в том, что когда вы используете streams в последнем фрагменте, вы буферизуете все фрагменты, нарушая цель использования потоков.
Вместо этого вы должны сделать .pipe прямо к ответу, таким образом используемая память будет довольно низкой.
app.get("/test-download/:id", (req, res) => {
const params = {
Bucket: bucketName,
Key: req.params.id
};
s3.getObject(params)
.createReadStream()
.pipe(res);
});
Имейте в виду, что сейчас вы не отвечаете на объект JSON, поэтому клиент должен быть изменен.
Зачем вам нужно конвертировать его в JSON? Вы используете new Uint8Array(res.data.data.Body.data) во внешнем интерфейсе. просто используйте new Uint8Array(res.data) или new Uint8Array(res.data.data) (я не знаю, как получить доступ к ответу в этой библиотеке)
Я вижу следующие данные: "�0�wO��]j��hW��A�Ap���:��<B^�tS�t+��"���/�n��3Qҕ �$�=?�q*�m&;:���h�ZȆY��K3Ʃ�2 뗷�K�(5" Uint8Array(res.data) просто возвращает пустой массив. Аналогично, new Blob([new Uint8Array(res.data)]) возвращает пустой объект.
Это необработанный файл, который вы загружаете. Нажмите URL-адрес без аксиом, прямо в браузере, вы получите загруженный файл. Вы можете установить соответствующие заголовки для загрузки, content-disposition и так далее.
Указать вам где-нибудь для чего именно? Что произойдет, если вы просто войдете в :/test-download/54 в браузере?
Сам URL-адрес не будет работать, потому что это частный файл. Вот почему мы используем s3 с параметрами ведра.
Я не говорю URL-адрес s3, я имею в виду URL-адрес вашего маршрута... тот, который я написал в сценарии.
Благодаря Маркосу я пересмотрел код конвейера, который мы пробовали. Но теперь, полностью понимая ответ необработанных данных, который я получил от createReadStream().pipe(), я смог преобразовать данные.
Внешний код
app.get("/test-download/:id", (req, res) => {
var params = {
Bucket: bucketName,
Key: req.params.id
};
s3.getObject(params)
.createReadStream()
.pipe(res)
.on("finish", () => {
console.info("** done");
});
});
Внутренний код
downloadFile = (id, name, type) => {
axios
.get(
`/test-download/${id}`,
{ responseType: "arraybuffer" },
this.props.handleSnackBar(
"Your download has been started. Please wait."
)
)
.then(res => {
console.info(res);
download(res.data, `${name}.${type}`);
this.props.handleSnackBar("Your download is now ready.");
})
.catch(err => console.info(err));
};
То, что называется «Frontend code», на самом деле выглядит как «Backend code» и наоборот.
Это объяснило бы интересные данные ответа :) Итак, теперь код на стороне клиента должен преобразовать это в json?