Автор Джозеф Мава✏️
Введение
Изображения составляют значительную часть данных, передаваемых в интернете. Чаще всего клиентам приходится загружать файлы изображений со своих устройств на сервер. Чтобы убедиться, что пользователи загружают файлы изображений правильного типа, качества и размера, большинство веб-приложений имеют функции предварительного просмотра изображений.
В среде браузера клиенты могут инициировать загрузку изображений, просматривая файлы с помощью элемента input
или API перетаскивания. Затем вы можете использовать API URL
или API FileReader
для чтения файлов изображений и их предварительного просмотра.
Хотя предварительный просмотр изображений с помощью API URL
прост, использование API FileReader
может оказаться сложным. Поэтому в этой статье вы узнаете, как просматривать изображения в React-приложении с помощью API FileReader
. Мы рассмотрим как одиночный, так и пакетный предварительный просмотр изображений.
Содержание
- Как просматривать файлы изображений в React
- Введение в API
FileReader
- Как просмотреть одно изображение перед загрузкой в React с помощью API
FileReader
- Как просматривать несколько изображений перед загрузкой в React с помощью API
FileReader
- Заключение
Как просматривать файлы изображений в React
Если вы хотите добавить в свое веб-приложение функцию загрузки файлов, вам пригодится элемент input
типа file
. Он позволяет пользователям выбирать один или несколько файлов из хранилища на компьютере или мобильном устройстве:
<input type="file" accept="image/*" multiple />
Приведенный выше элемент input
при отображении в браузере будет выглядеть как кнопка. При нажатии на нее откроется встроенный в операционную систему диалог выбора файлов. Пользователь может выбрать файлы изображений для загрузки.
Элемент input
имеет атрибут accept
для ограничения типа файла. Его значением является строка, состоящая из спецификаторов типов файлов, разделенных запятыми. Значение атрибута accept
в элементе input
выше — image/*
. Он позволяет просматривать и загружать изображения любого формата.
Чтобы загружать файлы изображений определенного формата, можно ограничить значение атрибута accept
. Например, установив его значение image/png
или .png
, можно принимать только изображения PNG.
При установке булева атрибута multiple
в значение true
пользователь может выбрать несколько файлов изображений. С другой стороны, пользователь может просматривать только один файл изображения, если его значение false
. Стоит отметить, что значение булева атрибута true
, если атрибут присутствует на элементе, и false
, если опущен.
Браузер выдает событие change
после того, как пользователь завершает выбор файла. Поэтому вы должны прослушать событие change
на элементе input
. В React это можно сделать следующим образом:
<form>
<p>
<label htmlFor="file">Upload images</label>
<input
type="file"
id="file"
onChange={changeHandler}
accept="image/*"
multiple
/>
</p>
</form>
В обработчике события change
вы можете получить доступ к объекту FileList
. Это итерабельный список, элементами которого являются объекты File
. Объекты File
содержат метаданные, доступные только для чтения, такие как имя, тип и размер файла:
const changeHandler = (e) => {
const { files } = e.target
for (let i = 0; i < files.length; i++) {
const file = files[i]; // OR const file = files.item(i);
}
}
Введение в API FileReader
API FileReader
предоставляет интерфейс для асинхронного чтения содержимого файла из веб-приложения.
Как было показано в предыдущем разделе, вы можете использовать элемент input
типа file
для просмотра файлов с компьютера или мобильного устройства пользователя. Выбор файлов изображений таким способом возвращает объект FileList
, элементами которого являются объекты File
.
Затем API FileReader
использует объект File
для асинхронного чтения выбранного пользователем файла. Стоит отметить, что вы не можете использовать API FileReader
для чтения содержимого файла из файловой системы пользователя, используя имя файла.
API FileReader
имеет несколько асинхронных методов экземпляра для выполнения операций чтения. К этим методам относятся:
-
readAsArrayBuffer
-
readAsBinaryString
-
readAsDataURL
-
readAsText
В этой статье мы будем использовать метод readAsDataURL
. Метод readAsDataURL
принимает объект file в качестве аргумента и асинхронно считывает файл изображения в память как URL данных.
Он испускает событие change
после завершения операции read
:
const fileReader = new FileReader();
fileReader.onchange = (e) => {
const { result } = e.target;
}
fileReader.readAsDataURL(fileObject);
Подробное описание других методов экземпляра FileReader
вы можете прочитать в документации.
Как просмотреть одно изображение перед загрузкой в React
В этом разделе мы рассмотрим, как предварительно просмотреть одно изображение перед загрузкой в React с помощью API FileReader
. Предполагается, что у вас уже настроен проект React.
Код ниже показывает, как прочитать и просмотреть одно изображение в React с помощью API FileReader
. Мы используем элемент input
типа file
для просмотра файлов изображений. Поскольку мы хотим просматривать одно изображение, я опустил атрибут multiple
boolean на элементе input
:
import { useEffect, useState } from 'react';
const imageMimeType = /image/(png|jpg|jpeg)/i;
function App() {
const [file, setFile] = useState(null);
const [fileDataURL, setFileDataURL] = useState(null);
const changeHandler = (e) => {
const file = e.target.files[0];
if (!file.type.match(imageMimeType)) {
alert("Image mime type is not valid");
return;
}
setFile(file);
}
useEffect(() => {
let fileReader, isCancel = false;
if (file) {
fileReader = new FileReader();
fileReader.onload = (e) => {
const { result } = e.target;
if (result && !isCancel) {
setFileDataURL(result)
}
}
fileReader.readAsDataURL(file);
}
return () => {
isCancel = true;
if (fileReader && fileReader.readyState === 1) {
fileReader.abort();
}
}
}, [file]);
return (
<>
<form>
<p>
<label htmlFor='image'> Browse images </label>
<input
type="file"
id='image'
accept='.png, .jpg, .jpeg'
onChange={changeHandler}
/>
</p>
<p>
<input type="submit" label="Upload" />
</p>
</form>
{fileDataURL ?
<p className="img-preview-wrapper">
{
<img src={fileDataURL} alt="preview" />
}
</p> : null}
</>
);
}
export default App;
Как показано в примере выше, вы можете прослушивать событие change
на элементе input
. Обработчик события change
вызывается после того, как клиент завершает выбор файла. Вы можете получить доступ к объекту File
, представляющему выбранный файл, и обновить состояние в обработчике события.
Поскольку HTML-разметка в браузере является редактируемой, необходимо проверить MIME-тип выбранного файла перед началом процесса чтения. Хотя маловероятно, что обычный пользователь будет редактировать HTML-элементы на веб-странице, это не позволит никому легко сломать ваше приложение.
После загрузки файлов вам нужно будет выполнить аналогичную проверку на стороне сервера. На этом этапе вы также можете проверить размер выбранного файла, чтобы убедиться, что он не превышает максимальный предел.
Поскольку чтение выбранного файла является побочным эффектом, мы используем хук useEffect
. Как было показано в предыдущем разделе, вы начинаете с создания экземпляра FileReader
. Метод readAsDataURL
API FileReader
асинхронно считывает файл и выдает событие load
после завершения процесса чтения.
Возможно, что компонент размонтируется или перезагрузится до завершения процесса чтения. Если процесс чтения не завершен, необходимо прервать его перед размонтированием. Чтобы предотвратить утечку памяти, React запрещает обновление состояния после размонтирования компонента. Поэтому перед обновлением состояния в обработчике события load нам нужно проверить, смонтирован ли компонент.
Мы получаем доступ к данным файла в виде base64-кодированной строки и обновляем состояние после завершения процесса чтения. После этого можно выводить предварительный просмотр изображения. Для простоты я не добавил никаких стилей к элементу form
в приведенном примере.
Как сделать предварительный просмотр нескольких изображений перед загрузкой в React
В этом разделе мы рассмотрим, как просматривать несколько изображений перед загрузкой в React с помощью API FileReader
. Как и в предыдущем разделе, предполагается, что у вас уже есть созданный проект React.
Чтение и предварительный просмотр нескольких изображений аналогичен предварительному просмотру одного изображения. Мы немного изменим код в предыдущем разделе. Чтобы просмотреть и выбрать несколько файлов изображений, нужно установить значение булевого атрибута multiple
в true
на элементе input
.
Одно заметное отличие заключается в том, что мы перебираем объект FileList
в хуке useEffect
и читаем содержимое всех выбранных файлов перед обновлением состояния. Мы храним URL данных каждого файла изображения в массиве и обновляем состояние после чтения последнего файла.
Приведенный ниже код является модификацией предыдущего примера для пакетного предварительного просмотра изображений:
import { useEffect, useState } from "react";
const imageTypeRegex = /image/(png|jpg|jpeg)/gm;
function App() {
const [imageFiles, setImageFiles] = useState([]);
const [images, setImages] = useState([]);
const changeHandler = (e) => {
const { files } = e.target;
const validImageFiles = [];
for (let i = 0; i < files.length; i++) {
const file = files[i];
if (file.type.match(imageTypeRegex)) {
validImageFiles.push(file);
}
}
if (validImageFiles.length) {
setImageFiles(validImageFiles);
return;
}
alert("Selected images are not of valid type!");
};
useEffect(() => {
const images = [], fileReaders = [];
let isCancel = false;
if (imageFiles.length) {
imageFiles.forEach((file) => {
const fileReader = new FileReader();
fileReaders.push(fileReader);
fileReader.onload = (e) => {
const { result } = e.target;
if (result) {
images.push(result)
}
if (images.length === imageFiles.length && !isCancel) {
setImages(images);
}
}
fileReader.readAsDataURL(file);
})
};
return () => {
isCancel = true;
fileReaders.forEach(fileReader => {
if (fileReader.readyState === 1) {
fileReader.abort()
}
})
}
}, [imageFiles]);
return (
<div className="App">
<form>
<p>
<label htmlFor="file">Upload images</label>
<input
type="file"
id="file"
onChange={changeHandler}
accept="image/png, image/jpg, image/jpeg"
multiple
/>
</p>
</form>
{
images.length > 0 ?
<div>
{
images.map((image, idx) => {
return <p key={idx}> <img src={image} alt="" /> </p>
})
}
</div> : null
}
</div>
);
}
export default App;
Мы сохраняем ссылки на экземпляры FileReader
в массиве для отмены любого процесса чтения файлов в функции cleanup
при повторном рендеринге или размонтировании компонента, чтобы избежать утечек памяти.
При использовании библиотеки маршрутизации, такой как React Router, пользователь может перейти с текущей страницы, и компонент размонтируется до завершения процесса чтения файла. Поэтому необходимо выполнить очистку, как указано выше.
В приведенном выше примере мы асинхронно читаем файлы в цикле и обновляем состояние после этого. Из-за асинхронной природы процесса чтения файлов невозможно узнать, какой файл мы завершим читать последним. Поэтому перед обновлением состояния мы должны проверить количество прочитанных файлов в обработчике события load
. Того же самого можно добиться с помощью обещаний.
В приведенном ниже коде показана модификация хука useEffect
для использования обещаний вместо него. Это чище и проще, чем использование циклов, как в предыдущем методе:
useEffect(() => {
const fileReaders = [];
let isCancel = false;
if (imageFiles.length) {
const promises = imageFiles.map(file => {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReaders.push(fileReader);
fileReader.onload = (e) => {
const { result } = e.target;
if (result) {
resolve(result);
}
}
fileReader.onabort = () => {
reject(new Error("File reading aborted"));
}
fileReader.onerror = () => {
reject(new Error("Failed to read file"));
}
fileReader.readAsDataURL(file);
})
});
Promise
.all(promises)
.then(images => {
if (!isCancel) {
setImages(images);
}
})
.catch(reason => {
console.log(reason);
});
};
return () => {
isCancel = true;
fileReaders.forEach(fileReader => {
if (fileReader.readyState === 1) {
fileReader.abort()
}
})
}
}, [imageFiles]);
Заключение
Большинство веб-приложений, требующих загрузки изображений с устройства хранения данных клиента, также оснащены функциями предварительного просмотра изображений. Среди прочих причин, предварительный просмотр изображений гарантирует, что ваши клиенты загружают файлы изображений соответствующего типа, качества и размера.
Вы можете инициировать загрузку файлов с устройства клиента с помощью элемента input
типа file
или с помощью интерфейса перетаскивания. После выбора изображений их можно предварительно просмотреть с помощью API URL
или API FileReader
. Хотя использование API URL
может быть простым, API FileReader
не является таковым.
Как подчеркивалось в статье, вы можете просматривать изображения по одному или пакетно. Надеюсь, эта статья дала вам представление о предварительном просмотре изображений в React с помощью API FileReader
. Дайте мне знать, что вы думаете в разделе комментариев ниже.
Полная видимость производственных приложений React
Отладка приложений React может быть сложной задачей, особенно когда пользователи сталкиваются с проблемами, которые трудно воспроизвести. Если вы заинтересованы в мониторинге и отслеживании состояния Redux, автоматическом выявлении ошибок JavaScript, отслеживании медленных сетевых запросов и времени загрузки компонентов, попробуйте LogRocket.
LogRocket — это как видеорегистратор для веб- и мобильных приложений, записывающий буквально все, что происходит в вашем React-приложении. Вместо того чтобы гадать, почему возникают проблемы, вы можете собрать данные и отчитаться о том, в каком состоянии находилось ваше приложение в момент возникновения проблемы. LogRocket также отслеживает производительность вашего приложения, предоставляя такие показатели, как загрузка процессора клиента, использование памяти клиента и многое другое.
Пакет промежуточного ПО LogRocket Redux добавляет дополнительный уровень видимости пользовательских сессий. LogRocket регистрирует все действия и состояние ваших хранилищ Redux.
Модернизируйте отладку приложений React — начните мониторинг бесплатно.