Чтение файлов в браузере
File API - чтение файлов в браузере
Страница может прочитать файл с диска пользователя до отправки на сервер — проверить размер, показать превью аватара, распарсить CSV в таблицу. Браузер не даёт произвольный доступ к файловой системе — только к файлам, которые пользователь явно выбрал или перетащил.
Связь: отправка на сервер — FormData; перетаскивание — drag-and-drop.
Типовые сценарии File API
| Сценарий | Задача |
|---|---|
| Профиль | аватар, документы |
| Админка | импорт CSV |
| Публикация | превью изображения до upload |
Общий поток: выбор → проверка размера/типа на клиенте → превью → отправка на сервер. Ошибки формата и лимита лучше показывать до запроса, а не после ответа API.
Выбор файла через input
<label>
Аватар
<input type="file" id="avatar" accept="image/png,image/jpeg" />
</label>
<p id="preview"></p>
Разбор:
- В этом фрагменте используется конструкция
<label>как точка входа сценария. - Разметка задаёт опорные элементы интерфейса, к которым затем подключается JavaScript-логика.
- Ключевые вызовы и свойства опираются на стандартные API JavaScript/браузера, поэтому шаблон легко перенести в реальный проект.
- Такой пример удобно расширять обработкой ошибок, логированием и дополнительной валидацией входных данных.
| Атрибут | Смысл |
|---|---|
accept | подсказка диалогу (MIME или расширения) |
multiple | несколько файлов в input.files |
capture | на мобильных — камера вместо галереи |
Код ITЗагрузка примера кода…
Разбор:
- В этом фрагменте используется конструкция
const input = document.getElementById('avatar');как точка входа сценария. - Код показывает последовательность действий: получение данных, проверка условий и выполнение целевого действия.
- Подписка через
addEventListenerсвязывает поведение с действием пользователя и отделяет логику от HTML-разметки. - Ключевые вызовы и свойства опираются на стандартные API JavaScript/браузера, поэтому шаблон легко перенести в реальный проект.
- Такой пример удобно расширять обработкой ошибок, логированием и дополнительной валидацией входных данных.
input.files — живой FileList. После input.value = '' список очищается.
Объект File
Каждый выбранный файл — экземпляр File (наследник Blob).
| Свойство | Описание |
|---|---|
name | имя файла |
size | размер в байтах |
type | MIME (image/png, text/plain, может быть пустым) |
lastModified | метка времени |
console.log(`${file.name}: ${(file.size / 1024).toFixed(1)} КБ, ${file.type}`);
Разбор:
- Фрагмент выводит имя, размер в КБ и MIME-тип через
console.log(см. блок javascript выше). - Код показывает последовательность действий: получение данных, проверка условий и выполнение целевого действия.
- Ключевые вызовы и свойства опираются на стандартные API JavaScript/браузера, поэтому шаблон легко перенести в реальный проект.
- Такой пример удобно расширять обработкой ошибок, логированием и дополнительной валидацией входных данных.
Проверяйте size и type на клиенте для UX, но повторяйте проверку на сервере — клиентские ограничения обходятся.
FileReader
FileReader читает Blob/File асинхронно в строку или бинарные данные.
| Метод | Результат в reader.result |
|---|---|
readAsText(file, encoding?) | текст |
readAsDataURL(file) | строка data:image/png;base64,... — удобно для <img src> |
readAsArrayBuffer(file) | ArrayBuffer для бинарных форматов |
События — load (успех), error, abort. В современном коде можно обернуть в Promise:
function readAsDataURL(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = () => reject(reader.error);
reader.readAsDataURL(file);
});
}
async function showImagePreview(file, container) {
const url = await readAsDataURL(file);
container.innerHTML = `<img src="${url}" alt="Превью" width="120">`;
}
Разбор:
- В этом фрагменте используется конструкция
function readAsDataURL(file) {как точка входа сценария. - Код показывает последовательность действий: получение данных, проверка условий и выполнение целевого действия.
- Используется асинхронное выполнение (
async/await), поэтому интерфейс остаётся отзывчивым, пока операция не завершится. - Ключевые вызовы и свойства опираются на стандартные API JavaScript/браузера, поэтому шаблон легко перенести в реальный проект.
- Такой пример удобно расширять обработкой ошибок, логированием и дополнительной валидацией входных данных.
После вставки превью через data URL для больших файлов помните об памяти: для продакшена часто используют URL.createObjectURL(file):
const url = URL.createObjectURL(file);
img.src = url;
img.onload = () => URL.revokeObjectURL(url);
Разбор:
- В этом фрагменте используется конструкция
const url = URL.createObjectURL(file);как точка входа сценария. - Код показывает последовательность действий: получение данных, проверка условий и выполнение целевого действия.
- Ключевые вызовы и свойства опираются на стандартные API JavaScript/браузера, поэтому шаблон легко перенести в реальный проект.
- Такой пример удобно расширять обработкой ошибок, логированием и дополнительной валидацией входных данных.
createObjectURL короче по памяти, чем base64, для крупных изображений.
Несколько файлов
input.multiple = true;
input.addEventListener('change', () => {
for (const file of input.files) {
console.log(file.name);
}
});
Разбор:
- В этом фрагменте используется конструкция
input.multiple = true;как точка входа сценария. - Код показывает последовательность действий: получение данных, проверка условий и выполнение целевого действия.
- Подписка через
addEventListenerсвязывает поведение с действием пользователя и отделяет логику от HTML-разметки. - Ключевые вызовы и свойства опираются на стандартные API JavaScript/браузера, поэтому шаблон легко перенести в реальный проект.
- Такой пример удобно расширять обработкой ошибок, логированием и дополнительной валидацией входных данных.
Или Array.from(input.files).filter(...) для валидации каждого элемента.
Отправка на сервер
Файлы уходят через FormData без ручного кодирования:
const formData = new FormData();
formData.append('avatar', file);
formData.append('userId', '42');
await fetch('/api/upload', { method: 'POST', body: formData });
Разбор:
- В этом фрагменте используется конструкция
const formData = new FormData();как точка входа сценария. - Код показывает последовательность действий: получение данных, проверка условий и выполнение целевого действия.
- Используется асинхронное выполнение (
async/await), поэтому интерфейс остаётся отзывчивым, пока операция не завершится. - Ключевые вызовы и свойства опираются на стандартные API JavaScript/браузера, поэтому шаблон легко перенести в реальный проект.
- Такой пример удобно расширять обработкой ошибок, логированием и дополнительной валидацией входных данных.
Не задавайте заголовок Content-Type вручную — браузер добавит multipart/form-data с границей.
Для JSON-API иногда читают файл в base64 и кладут в JSON — это увеличивает трафик; предпочтительнее отдельный multipart-эндпоинт.
Перетаскивание файлов
Тот же объект File приходит из dataTransfer.files в событии drop на зоне загрузки (см. drag-and-drop). Логику чтения (FileReader / createObjectURL) можно вынести в общую функцию.
Ограничения и безопасность
- Скрипт не видит путь на диске (
C:\Users\...) — только имя и содержимое. - Чтение больших файлов блокирует не поток, но память — показывайте прогресс, режьте на части (
file.slice()+Blob) для очень больших загрузок. - Парсинг CSV/JSON из пользовательского файла — изолируйте ошибки, не выполняйте содержимое как код.
Практический сценарий — импорт CSV в интерфейс
Рабочий конвейер часто выглядит так:
- Пользователь выбирает
.csvчерез<input type="file" accept=".csv,text/csv">. - Клиент проверяет размер и тип файла.
FileReader.readAsTextчитает файл.- Скрипт валидирует заголовки и количество колонок.
- Предпросмотр первых 20 строк показывается в таблице.
- Подтверждённые данные отправляются на сервер батчами.
Этот сценарий хорошо дополняет валидацию форм и асинхронную обработку, где важно показывать пользователю понятный прогресс.
Частые ошибки
| Ошибка | Последствие | Что делать |
|---|---|---|
Проверка только расширения .csv | файл может быть другим по содержимому | дополнительно проверять MIME и структуру данных |
| Весь файл читается в память сразу | зависания на больших файлах | читать частями через slice() и обрабатывать порциями |
Использование innerHTML для содержимого файла | XSS-риски | выводить через textContent или санитайзер |
| Нет лимитов на размер в UI | плохой UX и перегрузка | заранее показывать допустимый максимум файла |
Краткий итог
Пользователь выбирает файл → File в input.files → FileReader или URL.createObjectURL для превью → FormData + fetch для отправки. Пример FormData и POST — Fetch / axios — типовые запросы. Клиентская проверка размера и типа дополняет, но не заменяет серверную.