Back

Работа с файлами с помощью FileReader API

Работа с файлами с помощью FileReader API

Когда пользователь выбирает файл в вашем веб-приложении, вы получаете объект File — но как на самом деле прочитать его содержимое? Именно для этого существует JavaScript FileReader API. В этой статье мы разберём, как работает FileReader, когда его использовать и чем он отличается от современных альтернатив.

Ключевые моменты

  • FileReader API читает содержимое File или Blob асинхронно, используя один из трёх методов: readAsText(), readAsArrayBuffer() или readAsDataURL().
  • FileReader использует событийную модель с событиями load, error, progress и другими для передачи результатов.
  • Вы можете обернуть FileReader в Promise для более удобной интеграции с кодом на async/await.
  • Современные методы Blob.text() и Blob.arrayBuffer() проще для базового чтения, но FileReader остаётся правильным выбором, когда вам нужно отслеживание прогресса, кодировка отличная от UTF-8 или base64 data URL.

Что такое FileReader API?

FileReader API — это браузерный интерфейс, который асинхронно читает содержимое объектов File или Blob. Он не может получить доступ к произвольным файлам в системе пользователя по пути — он работает только с файлами, которые пользователь явно выбрал через <input type="file"> или взаимодействие drag-and-drop.

const input = document.querySelector('input[type="file"]')

input.addEventListener('change', () => {
  const file = input.files[0]
  console.log(file.name, file.size, file.type)
})

Объект File наследуется от Blob, поэтому он имеет те же свойства плюс name и lastModified. Вы передаёте его напрямую в любой метод чтения FileReader.

Три основных метода чтения

Чтение локальных файлов в JavaScript с помощью FileReader означает выбор правильного формата вывода:

  • readAsText(file, encoding?) — Возвращает содержимое файла в виде строки. По умолчанию используется UTF-8. Полезно для CSV, JSON, обычного текста или любого текстового формата.
  • readAsArrayBuffer(file) — Возвращает необработанные бинарные данные в виде ArrayBuffer. Используйте это для изображений, аудио, PDF или любой бинарной обработки.
  • readAsDataURL(file) — Возвращает base64-кодированный data: URL. Удобно для предпросмотра изображений, но учтите, что результат примерно на 33% больше исходного файла из-за накладных расходов кодирования base64.

Избегайте readAsBinaryString() — он устарел. Используйте вместо него readAsArrayBuffer() для бинарных данных.

Событийная модель FileReader

FileReader работает асинхронно и взаимодействует через события. Основные из них:

СобытиеКогда срабатывает
loadstartНачинается чтение
progressПериодически срабатывает во время чтения
loadЧтение успешно завершено
errorЧтение завершилось с ошибкой
loadendЧтение завершено (успешно или с ошибкой)

Вот практический пример предпросмотра изображения перед загрузкой с использованием readAsDataURL():

function previewImage(file) {
  const reader = new FileReader()

  reader.addEventListener('load', () => {
    const img = document.querySelector('#preview')
    img.src = reader.result
  })

  reader.addEventListener('error', () => {
    console.error('Failed to read file:', reader.error)
  })

  reader.readAsDataURL(file)
}

Событие progress особенно полезно для отображения индикатора прогресса при работе с большими файлами:

reader.addEventListener('progress', (event) => {
  if (event.lengthComputable) {
    const percent = Math.round((event.loaded / event.total) * 100)
    progressBar.value = percent
  }
})

Обёртывание FileReader в Promise

Стиль колбэков FileReader может показаться неудобным в современном асинхронном коде. Простая обёртка решает эту проблему:

function readFileAsText(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    reader.addEventListener('load', () => resolve(reader.result))
    reader.addEventListener('error', () => reject(reader.error))
    reader.readAsText(file)
  })
}

// Использование
const text = await readFileAsText(file)

FileReader vs Blob.text() и Blob.arrayBuffer()

Современные браузеры поддерживают методы на основе промисов непосредственно в Blob (и, следовательно, в File):

// Современный подход — проще для базового чтения
const text = await file.text()
const buffer = await file.arrayBuffer()

Так когда же всё-таки стоит использовать FileReader? Выбирайте его, когда вам нужно:

  • Отслеживание прогресса через событие progress (хотя для локальных файлов некоторые браузеры могут генерировать лишь небольшое количество событий прогресса)
  • Поддержка кодировок отличных от UTF-8 через readAsText(file, 'ISO-8859-1')
  • Base64 data URL через readAsDataURL()
  • Более широкая поддержка устаревших браузеров

Для простого чтения текста или бинарных данных в современных приложениях Blob.text() и Blob.arrayBuffer() более удобны. Для предпросмотра изображений URL.createObjectURL(file) избегает расширения base64 и в целом более эффективен по памяти, чем readAsDataURL(). Просто не забудьте вызвать URL.revokeObjectURL(), когда объектный URL больше не нужен, чтобы избежать утечек памяти.

Заключение

FileReader API остаётся практичным инструментом для работы с объектами File в браузере — особенно когда вам нужны события прогресса, контроль кодировки или data URL. Для более простых случаев новые методы Blob требуют меньше кода. Знайте оба подхода, и вы сможете чисто обрабатывать любой сценарий чтения файлов.

Часто задаваемые вопросы

Нет. FileReader может читать только файлы, которые пользователь явно выбрал через элемент input или взаимодействие drag-and-drop. Модель безопасности браузера не позволяет JavaScript получать доступ к произвольным файлам в файловой системе по пути. Это ограничение существует для защиты конфиденциальности пользователя и безопасности системы.

readAsArrayBuffer возвращает необработанные бинарные данные, подходящие для обработки или манипуляций, таких как парсинг метаданных изображений или звуковых волн. readAsDataURL возвращает строку в кодировке base64, которую можно использовать напрямую как src изображения или фон CSS. Подход с data URL удобен, но создаёт вывод примерно на 33 процента больше исходного файла.

Если вам просто нужно текстовое содержимое и ваше приложение ориентировано на современные браузеры, Blob.text() проще, потому что он возвращает Promise напрямую. Используйте FileReader, когда вам нужно отслеживание прогресса для больших файлов, нужно указать кодировку отличную от UTF-8 или нужно поддерживать старые браузеры, в которых отсутствует метод Blob.text().

Если вы используете URL.createObjectURL для генерации URL предпросмотра, вызовите URL.revokeObjectURL после того, как изображение загрузилось или предпросмотр удалён. Если вы используете readAsDataURL, строка base64 управляется сборщиком мусора автоматически, но она потребляет больше памяти, чем объектный URL для того же файла.

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay