Чтение и обработка файлов через FileReader API

Веб-браузеры, поддерживающие FileReader и <input type=”file”>, позволяют пользователям загружать файлы.

В этом мануале вы познакомитесь с API-интерфейсами File, FileReader и FileReaderSync.

Требования

  • Не помешает базовое понимание методов JavaScript, EventListener и промисов.
  • Редактор кода.
  • Современный веб-браузер, поддерживающий File, FileReader и FileReaderSync.

Загрузка файла

Чтобы получить файл от пользователя, нам нужно использовать элемент <input>:

<input id="input" type="file" />

Этот код позволяет пользователям загружать файлы со своих машин.

Вот пример загрузки файла с помощью HTML-свойства <form>:

<form enctype="multipart/form-data" action="/upload" method="post">
  <input id="input" type="file" />
</form>

Для большего контроля обработчик загрузок вы можете использовать JavaScript вместо HTML <form>:

let file = document.getElementById('input').files[0];
let formData = new FormData();

formData.append('file', file);
fetch('/upload/image', {method: "POST", body: formData});

В этом подходе используются FormData и fetch.

Свойства Blob интерфейса File

В современных браузерах интерфейс File предлагает свойства и функции Blob. Эти функции позволяют нам читать файл.

  • .text() преобразует файл в поток, а затем в строку.
  • .stream() возвращает ReadableStream.
  • .arrayBuffer() возвращает ArrayBuffer, который содержит данные большого двоичного объекта в двоичной форме.
  • .slice() позволяет получать срезы файла.

Создайте новый файл myFile.txt с таким текстом:

Example file content.

Затем создайте новый файл file-blob-example.html и поместите в него:

<!DOCTYPE html>
<html>
<body>
  <input type="file" id="input" />

  <script>
    const streamToText = async (blob) => {
      const readableStream = await blob.getReader();
      const chunk = await readableStream.read();

      return new TextDecoder('utf-8').decode(chunk.value);
    };

    const bufferToText = (buffer) => {
      const bufferByteLength = buffer.byteLength;
      const bufferUint8Array = new Uint8Array(buffer, 0, bufferByteLength);

      return new TextDecoder().decode(bufferUint8Array);
    };

    document.getElementById('input').addEventListener('change', function(e) {
      let file = document.getElementById('input').files[0];

      (async () => {
        const fileContent = await file.text();

        console.log('.text()', fileContent);

        const fileContentStream = await file.stream();

        console.log('.stream()', await streamToText(fileContentStream));

        const buffer = await file.arrayBuffer();

        console.log('.buffer()', bufferToText(buffer));

        const fileSliceBlob = file.slice(0, file.length);
        const fileSliceBlobStream = await fileSliceBlob.stream();

        console.log('.slice() and .stream()', await streamToText(fileSliceBlobStream));
      })();
    });
  </script>
</body>
</html>

Откройте file-blob-example.html в своем веб-браузере и добавьте myFile.txt во входные данные. В консоли веб-разработчика можно посмотреть, как с помощью .text(), .stream(), .buffer() и .slice() считывается содержимое файла.

В этом подходе используются ReadableStreamTextDecoder() и Uint8Array()

Жизненный цикл и методы интерфейса FileReader

К FileReader прикреплено 6 основных событий:

  1. loadstart: срабатывает, когда мы начинаем загрузку файла.
  2. progress: срабатывает при чтении большого двоичного объекта в памяти.
  3. abort: срабатывает при вызове .abort.
  4. error: запускается при возникновении ошибки.
  5. load: запускается, если чтение прошло успешно.
  6. loadend: срабатывает, когда файл загружается, а error или abort не вызывается или load начинает новую операцию чтения.

Чтобы начать загрузку файла, у нас есть четыре метода:

  • readAsArrayBuffer(file): читает файл или большой двоичный объект как массив ArrayBuffer. Один из вариантов использования – отправка больших файлов рабочему процессу сервиса.
  • readAsBinaryString(file): читает файл как двоичную строку.
  • readAsText(file, format): читает файл как USVString (почти как строку); здесь вы можете дополнительно указать формат.
  • readAsDataURL(file): возвращает URL-адрес, по которому вы можете получить доступ к содержимому файла, он закодирован в Base64 и готов к отправке на ваш сервер.

Создайте новый файл filereader-example.html и поместите в него код, который использует метод readAsDataURL():

<!DOCTYPE html>
<html>
<head>
  <style>
    body {
      background: #000;
      color: white;
    }

    #progress-bar {
      margin-top: 1em;
      width: 100vw;
      height: 1em;
      background: red;
      transition: 0.3s;
    }
  </style>
</head>
<body>
  <input type="file" id="input" />
  <progress value="0" max="100" id="progress-bar"></progress>
  <div id="status"></div>
  <script>
    const changeStatus = (status) => {
      document.getElementById('status').innerHTML = status;
    }

    const setProgress = (e) => {
      const fr = e.target;
      const loadingPercentage = 100 * e.loaded / e.total;

      document.getElementById('progress-bar').value = loadingPercentage;
    }

    const loaded = (e) => {
      const fr = e.target;
      var result = fr.result;

      changeStatus('Finished Loading!');
      console.log('Result:', result);
    }

    const errorHandler = (e) => {
      changeStatus('Error: ' + e.target.error.name);
    }

    const processFile = (file) => {
      const fr = new FileReader();

      fr.readAsDataURL(file);
      fr.addEventListener('loadstart', changeStatus('Start Loading'));
      fr.addEventListener('load', changeStatus('Loaded'));
      fr.addEventListener('loadend', loaded);
      fr.addEventListener('progress', setProgress);
      fr.addEventListener('error', errorHandler);
      fr.addEventListener('abort', changeStatus('Interrupted'));
    }

    document.getElementById('input').addEventListener('change', (e) => {
      const file = document.getElementById('input').files[0];

      if (file) {
        processFile(file);
      }
    });
  </script>
</body>
</html>

Откройте filereader-example.html в своем веб-браузере и добавьте файл myFile.txt в input. На экране появится индикатор выполнения, визуализирующий процесс обработки файла. Если загрузка прошла успешно, появится сообщение Start Loading, затем Loaded и Finished Loading.

Использование интерфейса FileReaderSync

FileReader – это асинхронный API, который не будет блокировать основной поток при чтении файлов (мы же не хотим, чтобы наш пользовательский интерфейс переставал работать, когда браузер пытается прочитать очень большой файл). Однако существует синхронная версия FileReader под названием FileReaderSync. Использовать FileReaderSync можно только в Web Workers. У веб-воркеров есть собственный поток, поэтому они не блокируют основной поток. FileReaderSync использует те же методы, что и FileReader:

  • FileReaderSync.readAsArrayBuffer()
  • FileReaderSync.readAsBinaryString()
  • FileReaderSync.readAsText()
  • FileReaderSync.readAsDataURL()

Обработчиков событий здесь нет, потому что это синхронный интерфейс.

Заключение

В этой статье вы познакомились с API-интерфейсами File, FileReader и FileReaderSync.

Не забудьте проверить поддержку этих функций браузерами, которые использует ваша целевая аудитория, чтобы убедиться, что пользователи смогут использовать ваше приложение.

Tags:

Добавить комментарий