Чтение файлов в JavaScript

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

Современный API доступа к файловой системе

API доступа к файловой системе (File System Access API) предоставляет возможность чтения и записи данных из файлов и каталогов на локальной системе пользователя. Он доступен в большинстве браузеров на базе Chromium, таких как Chrome и Edge. Подробнее см. в статье API доступа к файловой системе (File System Access API ).

Поскольку API доступа к файловой системе совместим не со всеми браузерами, мы рекомендуем использовать browser-fs-access — вспомогательную библиотеку, которая использует новый API везде, где он доступен, и возвращается к устаревшим подходам, когда он недоступен.

Работа с файлами, классический способ

В этом руководстве показано, как взаимодействовать с файлами, используя устаревшие методы JavaScript.

Выберите файлы

Существует два основных способа выбора файлов: с помощью элемента ввода HTML и с помощью зоны перетаскивания .

HTML-элемент ввода

Самый простой способ выбора файлов — использовать элемент <input type="file"> , который поддерживается всеми основными браузерами. При щелчке по нему пользователь может выбрать один или несколько файлов, используя multiple интерфейс выбора файлов операционной системы. Когда пользователь завершает выбор файла или файлов, срабатывает событие change элемента. Доступ к списку файлов можно получить из event.target.files , представляющего собой объект FileList . Каждый элемент FileList является объектом File .

<!-- The `multiple` attribute lets users select multiple files. -->
<input type="file" id="file-selector" multiple>
<script>
  const fileSelector = document.getElementById('file-selector');
  fileSelector.addEventListener('change', (event) => {
    const fileList = event.target.files;
    console.log(fileList);
  });
</script>

В следующем примере пользователь может выбрать несколько файлов, используя встроенный в операционную систему интерфейс выбора файлов, а затем вывести каждый выбранный файл на консоль.

Ограничить типы файлов, которые пользователи могут выбирать

В некоторых случаях может потребоваться ограничить типы файлов, которые могут выбирать пользователи. Например, приложение для редактирования изображений должно принимать только изображения, а не текстовые файлы. Чтобы установить ограничения по типам файлов, добавьте атрибут accept к элементу input, чтобы указать, какие типы файлов принимаются:

<input type="file" id="file-selector" accept=".jpg, .jpeg, .png">

Пользовательское перетаскивание

В некоторых браузерах элемент <input type="file"> также является целью перетаскивания, позволяя пользователям перетаскивать файлы в ваше приложение. Однако эта цель перетаскивания небольшая и может быть неудобной в использовании. Вместо этого, после предоставления основных функций с помощью элемента <input type="file"> , вы можете создать большую настраиваемую область перетаскивания.

Выберите зону высадки

Поверхность для переноса зависит от дизайна вашего приложения. Возможно, вы захотите, чтобы только часть окна была поверхностью для переноса, но можно использовать всё окно целиком.

Скриншот Squoosh, веб-приложения для сжатия изображений.
Squoosh превращает все окно в зону выпадения.

Приложение для сжатия изображений Squoosh позволяет пользователю перетаскивать изображение в любое место окна и, щёлкнув по нему, вызывать элемент <input type="file"> . Независимо от того, какую область вы выберете, убедитесь, что пользователю понятно, что он может перетаскивать файлы на эту поверхность.

Определите зону сброса

Чтобы сделать элемент зоной перетаскивания, создайте прослушиватели двух событий: dragover и drop . Событие dragover обновляет пользовательский интерфейс браузера, визуально указывая на то, что перетаскивание создаёт копию файла. Событие drop срабатывает после того, как пользователь отпускает файлы на поверхность. Как и в случае с элементом ввода, доступ к списку файлов можно получить из event.dataTransfer.files , который представляет собой объект FileList . Каждый элемент FileList является объектом File .

const dropArea = document.getElementById('drop-area');

dropArea.addEventListener('dragover', (event) => {
  event.stopPropagation();
  event.preventDefault();
  // Style the drag-and-drop as a "copy file" operation.
  event.dataTransfer.dropEffect = 'copy';
});

dropArea.addEventListener('drop', (event) => {
  event.stopPropagation();
  event.preventDefault();
  const fileList = event.dataTransfer.files;
  console.log(fileList);
});

event.stopPropagation() и event.preventDefault() останавливают поведение браузера по умолчанию и позволяют выполнить ваш код. Без них браузер ушёл бы с вашей страницы и открыл бы файлы, которые пользователь переместил в окно браузера.

Для наглядной демонстрации обратитесь к разделу Пользовательское перетаскивание .

А как насчет каталогов?

К сожалению, не существует хорошего способа доступа к каталогу с помощью JavaScript.

Атрибут webkitdirectory элемента <input type="file"> позволяет пользователю выбрать каталог или каталоги. Он поддерживается большинством основных браузеров, за исключением Firefox для Android и Safari для iOS.

Если функция перетаскивания включена, пользователь может попытаться перетащить каталог в зону перетаскивания. При срабатывании события перетаскивания создается объект File для каталога, но доступ к файлам в нем не предоставляется.

Чтение метаданных файла

Объект File содержит метаданные о файле. Большинство браузеров предоставляют имя файла, его размер и тип MIME, хотя в зависимости от платформы разные браузеры могут предоставлять различную или дополнительную информацию.

function getMetadataForFileList(fileList) {
  for (const file of fileList) {
    // Not supported in Safari for iOS.
    const name = file.name ? file.name : 'NOT SUPPORTED';
    // Not supported in Firefox for Android or Opera for Android.
    const type = file.type ? file.type : 'NOT SUPPORTED';
    // Unknown cross-browser support.
    const size = file.size ? file.size : 'NOT SUPPORTED';
    console.log({file, name, type, size});
  }
}

Увидеть это в действии можно в демонстрационном примере input-type-file .

Прочитать содержимое файла

Используйте FileReader для чтения содержимого объекта File в память. Вы можете указать FileReader читать файл как массив , URL-адрес данных или текст :

function readImage(file) {
  // Check if the file is an image.
  if (file.type && !file.type.startsWith('image/')) {
    console.log('File is not an image.', file.type, file);
    return;
  }

  const reader = new FileReader();
  reader.addEventListener('load', (event) => {
    img.src = event.target.result;
  });
  reader.readAsDataURL(file);
}

В этом примере File , предоставленный пользователем, считывается, преобразуется в URL-адрес данных и используется для отображения изображения в элементе img . Чтобы узнать, как проверить, что пользователь выбрал файл изображения, обратитесь к демонстрационному примеру read-image-file .

Отслеживать ход чтения файла

При чтении больших файлов может быть полезно предоставить пользователю пользовательский интерфейс, показывающий, насколько продвинулось чтение. Для этого используйте событие progress , предоставляемое классом FileReader . Событие progress имеет два свойства: loaded (объем прочитанных данных) и total (объем данных для чтения).

function readFile(file) {
  const reader = new FileReader();
  reader.addEventListener('load', (event) => {
    const result = event.target.result;
    // Do something with result
  });

  reader.addEventListener('progress', (event) => {
    if (event.loaded && event.total) {
      const percent = (event.loaded / event.total) * 100;
      console.log(`Progress: ${Math.round(percent)}`);
    }
  });
  reader.readAsDataURL(file);
}

Главное изображение Винсента Ботты из Unsplash