Legendarniy
@Legendarniy

Загрузка файла по чанкам?

Изначально, загрузку сделал вот так:
export function сonvertFilesToByteArray(e) {
  const MAX_FILE_SIZE = 1024 * 1024 * 50; // 50MB
  const files = Object.keys(e.target.files);
  const asyncReadFile = eachFile =>
    new Promise((resolve, reject) => {
      if (e.target.files[eachFile].size > MAX_FILE_SIZE) {
        return reject([{ message: `Файл ${e.target.files[eachFile].name} слишком большой` }]);
      }
      const reader = new FileReader();
      const targetFileInfo = {
        contentType: e.target.files[eachFile].type,
        filename: e.target.files[eachFile].name,
      };
      reader.readAsArrayBuffer(e.target.files[eachFile]);
      reader.onload = () => {
        resolve({ ...targetFileInfo, body: Array.from(new Uint8Array(reader.result)) });
      };
      reader.onerror = error => reject(error);
    });

  return Promise.all(files.map(asyncReadFile));
}


Тут в константе files определяю, сколько у меня файлов и к каждому из них применяю функцию.

Но при таком подходе, не смотря на то, что использую Promise.all, при загрузке файла/файлов которые имеют размерность больше ~2МБ, то страница фризится, нельзя никак взаимодействовать с ней (скроллить есть возможность). Кроме как реализации, когда каждый файл бьётся по чанкам ничего в голову не пришло, чтобы исправить ситуацию.
Для этого переписал код на такой
export function сonvertFilesToByteArray(e) {
  const MAX_FILE_SIZE = 1024 * 1024 * 50; // 50MB
  const files = Object.keys(e.target.files);
  const asyncReadFile = eachFile =>
    new Promise((resolve, reject) => {
      if (e.target.files[eachFile].size > MAX_FILE_SIZE) {
        return reject([{ message: `Файл ${e.target.files[eachFile].name} слишком большой` }]);
      }
      const file = e.target.files[eachFile];
      let offset = 0;
      console.log(offset, 'offset', file.size, 'size');
      const defaultChunkSize = 64 * 1024; // bytes
      const fileReader = new FileReader();
      const blob = file.slice(offset, offset + defaultChunkSize);
      const isEndOfFile = () => offset >= file.size;
      const testEndOfFile = () => {
        if (isEndOfFile()) {
          console.log('Done reading file');
        }
      };
      fileReader.readAsArrayBuffer(blob);
      fileReader.onloadend = (event) => {
        const target = (event.target);
        if (target.error == null) {
          const result = target.result;
          offset += result.length;
          testEndOfFile();
          console.log(result, 'result');
          resolve(result);
        } else {
          reject(target.error);
        }
      };
    });

  return Promise.all(files.map(asyncReadFile));
}


Но тут я уперся в то, что не понимаю, как прочитать файл по чанкам (если он больше одного) и собрать его воедино, чтобы на выходе получить массив байтов и как в первом примере скомпоновать объект вида
{contentType: 'plain/text', filename: 'blabla', body: и вот тут мой буффер вида [123, 456, 23, ...] }

Который при завершении чтении всех файлов перебросится наружу, где написал следующий код
handleFileUpload = (e) => {
    сonvertFilesToByteArray(e)
      .then((result) => {
        runInAction(() => {
          this.files = [
            ...this.files,
            ...result,
          ];
        });
      })
      .catch(err => runInAction(() => {
        this.errors = [...this.errors, err[0].message];
      }));
  }


Комрады, выручайте :)
  • Вопрос задан
  • 1717 просмотров
Пригласить эксперта
Ответы на вопрос 2
Stalker_RED
@Stalker_RED
А если не разбивать на чанки, а вместо этого вынести upload в webworker?
Ответ написан
bingo347
@bingo347 Куратор тега JavaScript
Crazy on performance...
Для своей либы ws-api писал вот такую загрузку чанками:
https://github.com/bingo347/ws-api/blob/master/lib...
вот тут использование: https://github.com/bingo347/ws-api/blob/master/lib...
но собирается оно у меня уже на сервере node.js в ReadableStream:
https://github.com/bingo347/ws-api/blob/master/lib...
https://github.com/bingo347/ws-api/blob/master/lib...

покопайтесь в коде, думаю найдете себе решение, т.к. задача очень похожа
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы