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

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

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

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

Поскольку API доступа к файловой системе совместим не со всеми браузерами, мы рекомендуем использовать браузер-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 type="file" id="file-selector" accept=".jpg, .jpeg, .png">

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

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

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

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

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

Приложение сжатия изображений 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.

Если перетаскивание включено, пользователь может попытаться перетащить каталог в зону перетаскивания. Когда срабатывает событие drop, оно включает объект 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-адрес данных и используется этот URL-адрес данных для отображения изображения в элементе img . Чтобы узнать, как проверить, что пользователь выбрал файл изображения, обратитесь к демонстрации read-image-file .

Следите за ходом чтения файла

При чтении больших файлов может быть полезно предоставить пользователю некоторый UX, чтобы сообщить пользователю, как далеко продвинулось чтение. Для этого используйте событие 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