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

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

Работа с элементом <input type="file">

Базовый способ получения файлов от пользователя — использование HTML-элемента <input> с атрибутом type="file":

import { h } from 'preact';
import { useState } from 'preact/hooks';

function FileUploader() {
    const [file, setFile] = useState(null);

    const handleFileChange = (event) => {
        const selectedFile = event.target.files[0];
        setFile(selectedFile);
    };

    return (
        <div>
            <input type="file" onCha nge={handleFileChange} />
            {file && <p>Выбран файл: {file.name}</p>}
        </div>
    );
}

export default FileUploader;

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

  • event.target.files возвращает объект FileList, содержащий все выбранные файлы.
  • Для множественного выбора используется атрибут multiple.
  • File содержит свойства name, size, type и lastModified.

Множественный выбор файлов

<input type="file" multiple onCha nge={(e) => console.log(e.target.files)} />

e.target.files в этом случае можно обрабатывать как массивоподобный объект, используя методы Array.from() для работы с массивами:

const filesArray = Array.from(event.target.files);
filesArray.forEach(file => console.log(file.name, file.size));

Загрузка файлов на сервер

Для отправки файлов на сервер используется FormData и fetch. В Preact это интегрируется через обработчики событий:

const uploadFile = async (file) => {
    const formData = new FormData();
    formData.append('file', file);

    try {
        const response = await fetch('/upload', {
            method: 'POST',
            body: formData,
        });

        if (!response.ok) throw new Error('Ошибка при загрузке');
        console.log('Файл успешно загружен');
    } catch (error) {
        console.error(error);
    }
};

Обработка файлов в компоненте может быть реализована так:

const handleUpload = () => {
    if (file) {
        uploadFile(file);
    }
};

Прогресс загрузки

Для отображения прогресса используется объект XMLHttpRequest:

const uploadFileWithProgress = (file, onProgress) => {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('POST', '/upload');

        xhr.upload.onprogr ess = (event) => {
            if (event.lengthComputable && onProgress) {
                const percent = Math.round((event.loaded / event.total) * 100);
                onProgress(percent);
            }
        };

        xhr.onl oad = () => (xhr.status === 200 ? resolve(xhr.response) : reject(xhr.statusText));
        xhr.oner ror = () => reject(xhr.statusText);

        const formData = new FormData();
        formData.append('file', file);
        xhr.send(formData);
    });
};

Использование в компоненте:

const [progress, setProgress] = useState(0);

const handleUpload = () => {
    if (file) {
        uploadFileWithProgress(file, setProgress);
    }
};

Ограничение размера и типа файлов

Для контроля над загружаемыми файлами применяются проверки:

const allowedTypes = ['image/jpeg', 'image/png'];
const maxSize = 5 * 1024 * 1024; // 5 МБ

const handleFileChange = (event) => {
    const selectedFile = event.target.files[0];

    if (!allowedTypes.includes(selectedFile.type)) {
        console.error('Неподдерживаемый формат');
        return;
    }

    if (selectedFile.size > maxSize) {
        console.error('Файл слишком большой');
        return;
    }

    setFile(selectedFile);
};

Отображение превью изображений

Для визуализации выбранного изображения используется URL.createObjectURL:

const [preview, setPreview] = useState(null);

const handleFileChange = (event) => {
    const selectedFile = event.target.files[0];
    if (selectedFile && selectedFile.type.startsWith('image/')) {
        setPreview(URL.createObjectURL(selectedFile));
        setFile(selectedFile);
    }
};

return (
    <div>
        <input type="file" onCha nge={handleFileChange} />
        {preview && <img src={preview} alt="Превью" width={200} />}
    </div>
);

Управление состоянием и очистка

Для сброса выбранного файла важно очищать состояние и ссылку на объект URL:

const clearFile = () => {
    setFile(null);
    if (preview) {
        URL.revokeObjectURL(preview);
        setPreview(null);
    }
};

Совмещение с drag-and-drop

Preact позволяет реализовать drag-and-drop загрузку:

const handleDrop = (event) => {
    event.preventDefault();
    const droppedFile = event.dataTransfer.files[0];
    setFile(droppedFile);
};

return (
    <div 
        onDragO ver={(e) => e.preventDefault()} 
        onD rop={handleDrop} 
        style={{ border: '2px dashed #ccc', padding: '20px' }}
    >
        Перетащите файл сюда
    </div>
);

Важные моменты производительности

  • Не хранить содержимое больших файлов в состоянии — достаточно ссылки на объект File.
  • Для множества файлов использовать массивы и map для отображения списка.
  • Освобождать ресурсы через URL.revokeObjectURL после использования превью.

Фреймворк Preact не ограничивает работу с файлами, но благодаря своей легковесной реактивной системе позволяет создавать быстрые и отзывчивые компоненты загрузки. Правильное использование состояния, FormData, fetch и событий drag-and-drop обеспечивает полноценную интеграцию загрузки файлов в современное веб-приложение.