Я пытался использовать класс JS XMLHttpRequest
для загрузки файлов. Сначала я пробовал что-то вроде этого:
const file = thisFunctionReturnsAFileObject();
const request = new XMLHttpRequest();
request.open('POST', '/upload-file');
const rawFileData = await file.arrayBuffer();
request.send(rawFileData);
Приведенный выше код работает (ура!) И отправляет необработанные двоичные данные файла на мой сервер.
Однако ... . Он использует ТОННУ памяти (потому что весь файл хранится в памяти, а JS не особенно удобен для памяти) ... Я обнаружил, что на моей машине (16 ГБ ОЗУ) я не мог отправлять файлы большего размера чем ~ 100 МБ, потому что JS выделит слишком много памяти, а вкладка Chrome взломает sh с кодом SIGILL.
Итак, я подумал, что было бы неплохо использовать здесь ReadableStreams. В моем случае у него достаточно хорошая совместимость с браузером (https://caniuse.com/#search = ReadableStream ), и мой компилятор TypeScript сказал мне, что request.send (...) поддерживает ReadableStreams (позже я пришел к выводу, что это неверно ). В итоге у меня получился такой код:
const file = thisFunctionReturnsAFileObject();
const request = new XMLHttpRequest();
request.open('POST', '/upload-file');
const fileStream = file.stream();
request.send(fileStream);
Но мой компилятор TypeScript предал меня (что было больно), и я получил "[object ReadableStream]" на моем сервере ಠ_ಠ.
Я все еще не знаю Я слишком много изучал вышеуказанный метод, поэтому я не уверен, есть ли способ сделать это. Я также очень признателен за помощь в этом!
Разделение запроса на фрагменты было бы оптимальным решением, поскольку после отправки фрагмента мы можем удалить его из памяти до того, как будет получен весь запрос.
Я искал и искал, но пока не нашел способа сделать это (вот почему я здесь ...). Что-то вроде этого в псевдокоде было бы оптимальным:
const file = thisFunctionReturnsAFileObject();
const request = new XMLHttpRequest();
request.open('POST', '/upload-file');
const fileStream = file.stream();
const fileStreamReader = fileStream.getReader();
const sendNextChunk = async () => {
const chunk = await fileStreamReader.read();
if (!chunk.done) { // chunk.done implies that there is no more data to be read
request.writeToBody(chunk.value); // chunk.value is a Uint8Array
} else {
request.end();
break;
}
}
sendNextChunk();
Я бы хотел, чтобы этот код отправлял запрос по частям и завершал запрос, когда все куски отправлены.
Самый полезный ресурс, который я пробовал, но не работал:
Метод потоковой передачи данных из браузера на сервер через HTTP
Не работал, потому что:
- Мне нужно решение для работы в одном запросе
- Я не могу использовать RTCDataChannel , это должен быть простой HTTP-запрос (есть ли Другой способ сделать это, кроме XMLHttpRequest?)
- Мне нужно, чтобы он работал в современных Chrome / Firefox / Edge et c. (нет IE поддержка в порядке)
Изменить: я не хочу использовать составную форму (класс FormData). Я хочу отправлять фактические двоичные данные, считанные из файлового потока, кусками.