DevGang
Авторизоваться

Как загрузить и просмотреть изображения в ReactJS?

Загрузка и предварительный просмотр изображений являются фундаментальными требованиями любого современного приложения, ориентированного на пользователя. Будь то Facebook, Instagram, WhatsApp, Twitter, LinkedIn, как бы вы ни называли, оно у вас есть.

В этой статье мы узнаем, как обрабатывать загрузку и предварительный просмотр изображений в приложении ReactJS. Просто обратите внимание: мы не будем использовать для этого какую-либо внешнюю (стороннюю) библиотеку. Скорее, мы построим его с нуля, чтобы понять, что происходит под капотом. Со мной? Приступим к настройке.

Если вы хотите учиться по видеоконтенту, эта статья доступна в виде видеоурока. Взгляни, пожалуйста. 🙂

Настройте приложение ReactJS с помощью Vite и TailwindCSS.

Мы будем использовать TailwindCSS с React и по традиции создадим каркас проекта React с помощью Vite. Хорошей новостью является то, что вам не придется выполнять всю установку и настройку с нуля. Вы можете использовать этот репозиторий шаблонов для создания репозитория вашего проекта с помощью Vite и TailwindCSS, настроенных с помощью React.

Нажмите кнопку Use this template в правом верхнем углу репозитория шаблонов. Вы перейдете на следующую страницу, чтобы предоставить информацию о новом репозитории.

На следующей странице укажите новое имя репозитория и подходящее описание и создайте репозиторий. В этой статье назовем новый репозиторий image-uploader.

Теперь клонируйте новый репозиторий, чтобы получить папку проекта локально. Измените каталог на папку проекта и установите необходимые зависимости, используя npm/yarn/pnpm.

Убедитесь, что у вас установлен Node.js версии 18 или выше. Вы можете попробовать команду node -v, чтобы узнать установленную версию. Если вы хотите установить или обновить Node.js до необходимой версии, сделайте это отсюда.
## With NPM
npm install

## With Yarn
yarn install

## With PNPM
pnpm install

Теперь вы можете запустить проект локально, используя

## With NPM
npm run dev

## With Yarn
yarn dev

## With PNPM
pnpm run dev

Приложение будет доступно для доступа по URL-адресу http://localhost:5173/.

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

Заполните структуру загрузчика

Создайте каталог компонентов в каталоге src. Теперь создайте файл ImageUpload.jsx в папке src/comComponents. Этот файл будет содержать компонент React для загрузки и предварительного просмотра изображений.

Прежде чем мы начнем писать код для загрузки изображений, нам нужно кое-что сделать.

  • default image, которое сообщает нашим пользователям о загрузчике изображений.
  • edit icon, позволяющий вызвать браузер файлов для выбора изображения.
  • loading gif, который мы будем использовать в качестве заполнителя во время загрузки файла.

Скопируйте файлы изображений отсюда в каталог src/assets вашего проекта. Теперь вернитесь к файлу ImageUpload.jsx и начните кодировать компонент.

Сначала импортируйте изображения, которые мы только что скопировали. Нам также понадобятся хуки useState и useRef из React. Итак, скопируйте и вставьте следующие инструкции импорта в начало файла компонента:

import { useState, useRef } from 'react';
import DefaultImage from "../assets/upload-photo-here.png";
import EditIcon from "../assets/edit.svg";
import UploadingAnimation from "../assets/uploading.gif";

Теперь давайте создадим функцию компонента прямо под операторами импорта.

// --- Import statements as belore

const ImageUpload = () => {
    const [avatarURL, setAvatarURL] = useState(DefaultImage);
        return (
            <div className="relative h-96 w-96 m-8">
              <img 
                src={avatarURL}
                alt ="Avatar"
                className="h-96 w-96 rounded-full" />

              <form id="form" encType='multipart/form-data'>
                <button
                  type='submit'
                  className='flex-center absolute bottom-12 right-14 h-9 w-9 rounded-full'>
                  <img
                    src={EditIcon}
                    alt="Edit"
                    className='object-cover' />
                </button>
              </form>  
            </div>
         )
}

export default ImageUpload;

Давайте разберемся в приведенном выше фрагменте кода:

  • Мы создали функциональный компонент реагирования (называемый ImageUpload). Компонент имеет состояние под названием avatarURL, которому присвоено начальное значение — изображение по умолчанию. Мы сделали это, чтобы страница не была тупой и пустой, и мы показываем нашим пользователям сообщение о загрузке с этим изображением по умолчанию.
  • Мы изменим значение состояния позже программно, когда пользователь начнет загрузку изображения и когда загрузка изображения завершится.
  • Далее у нас есть довольно простой JSX, который показывает это изображение по умолчанию, используя значение состояния avatarURL. У нас также есть форма с кнопкой и значком редактирования внизу. Мы вводим эту форму заранее, чтобы через некоторое время мы могли обрабатывать ввод файла.
  • Наконец, мы экспортировали компонент ImageUpload, чтобы можно было импортировать его в файл App.jsx для рендеринга.

Откройте файл App.jsx и замените его содержимое следующим фрагментом кода:

import ImageUpload from "./components/ImageUpload"

function App() {

  return (
    <div className="flex justify-center">
      <ImageUpload />
    </div>
  )
}

export default App;

Здесь мы импортировали компонент ImageUpload и использовали его в JSX компонента приложения. Мы окружили его DIV, чтобы лучше выровнять его на странице.

Далее, при желании, вы можете изменить несколько вещей в файле index.html. Мы изменили название приложения. Вы можете внести изменения в тег <title> под тегом <head>...</head>. Кроме того, мы добавили несколько классов для <body>, чтобы получить более темный фон (большинству людей в наши дни нравится темный режим!).

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>React Image Uploader</title>
  </head>
  <body class="bg-black bg-opacity-90">
    <div id="root"></div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>

Вы можете увидеть желтое округлое изображение с текстом Upload и hand icon, указывающий на edit icon, указывающий на то, что вы можете нажать на значок редактирования, чтобы начать процесс загрузки. Круто, да?

Давайте теперь обработаем тип входного файла, чтобы выбрать изображение для загрузки.

Работа с выбором файла

Теперь нам нужно добавить в наш компонент средство выбора файлов. Мы будем использовать элемент ввода HTML с файлом типа. Добавьте файл типа ввода внутри элемента <form> в JSX, как показано ниже.

const ImageUpload = () => {
    // --- All code like before

    return (
        {/* --- All code like before */}

        <form id="form" encType='multipart/form-data'>
            {/* --- The Button code like before */}

            <input 
              type="file"
              id="file" />
        </form>
    )
}

Если мы сейчас перейдем к пользовательскому интерфейсу, мы увидим средство выбора файла с возможностью Choose file. Вы можете нажать на нее, чтобы выбрать файлы. Это работает, но выглядит не очень хорошо. Кроме того, мы не хотим, чтобы наши пользователи нажимали на эту кнопку для выбора файлов, вместо этого им следует выполнить ту же операцию, щелкнув значок редактирования, который мы добавили ранее.

Настройка загрузчика с помощью ссылки (useRef)

Давайте это исправим. Нам нужно сделать две вещи:

  • Скрыть тип входного файла.
  • Обратитесь к типу входного файла с помощью значка редактирования, чтобы, когда мы нажимаем на значок редактирования, он имитировал нажатие кнопки Choose file типа входного файла (да, даже если он скрыт!).

React предоставляет нам стандартный хук под названием useRef, который позволяет ссылаться на элемент DOM и получать доступ к его свойствам. Это означает, что мы можем создать ссылку на тип ввода файла с помощью ловушки useRef и использовать эту ссылку для имитации действия щелчка по щелчку значка редактирования. Звучит многообещающе, но сбивает с толку? Не волнуйтесь, давайте посмотрим код и разберемся.

Если вы ищете более глубокое понимание хука useRef, у меня есть базовое видео. Пожалуйста, проверьте это.

Сначала скройте ввод файла, добавив к нему hidden свойство.

<input 
   type="file"
   id="file"
   hidden />

Теперь создайте ссылку, используя хук useRef в верхней части вашего компонента.

const ImageUpload = () => {
  const [avatarURL, setAvatarURL] = useState(DefaultImage);

  const fileUploadRef = useRef(null);

  // --- Rest of the code as is below
}

Добавьте этот fileUploadRef в качестве значения атрибута ref во входной файл.

 <input 
    type="file"
    id="file"
    ref={fileUploadRef}
    hidden />

Так. с этого момента fileUploadRef.current будет ссылаться на элемент ввода файла. Мы будем использовать его для имитации события щелчка.

Добавьте свойство onClick к <button> внутри формы и добавьте в качестве его значения функцию обработчика событий handleImageUpload.

<form id="form" encType='multipart/form-data'>
    <button
        type='submit'
        onClick={handleImageUpload}
        className='flex-center absolute bottom-12 right-14 h-9 w-9 rounded-full'>
            <img
                src={EditIcon}
                alt="Edit"
                className='object-cover' />
    </button>
</form>

Последнее, что осталось — это осквернить функцию handleImageUpload.

 const handleImageUpload = (event) => {
    event.preventDefault();
    fileUploadRef.current.click();
  }

Как вы видите в приведенном выше коде, теперь мы вызываем функцию click(), используя fileUploadRef.current. Это означает, что произойдет щелчок по вводу файла, и мы увидим, что средство выбора файла открывается, когда кто-то нажимает на значок редактирования.

На этом этапе код компонента ImageUpload будет таким:

import { useState, useRef } from "react";
import DefaultImage from "../assets/upload-photo-here.png";
import EditIcon from "../assets/edit.svg";
import UploadingAnimation from "../assets/uploading.gif";

const ImageUpload = () => {
  const [avatarURL, setAvatarURL] = useState(DefaultImage);
  const fileUploadRef = useRef();

  const handleImageUpload = (event) => {
    event.preventDefault();
    fileUploadRef.current.click();
  };

  return (
    <div className="relative h-96 w-96 m-8">
      <img 
        src={avatarURL} 
        alt="Avatar" 
        className="h-96 w-96 rounded-full" />

      <form id="form" encType="multipart/form-data">
        <button
          type="submit"
          onClick={handleImageUpload}
          className="flex-center absolute bottom-12 right-14 h-9 w-9 rounded-full"
        >
          <img 
            src={EditIcon} 
            alt="Edit" 
            className="object-cover" />
        </button>
        <input 
            type="file" 
            id="file" 
            ref={fileUploadRef} 
            hidden />
      </form>
    </div>
  );
};

export default ImageUpload;

Все это здорово, но заметили ли вы, что на самом деле ничего не происходит, когда вы выбираете изображение и нажимаете кнопку Open в средстве выбора файлов? Давайте разберемся с этой частью сейчас.

Обработка события изменения файла

Чтобы захватить выбранный файл и его информацию, нам нужно добавить обработчик изменений на входе файла. Добавьте свойство onChange во входной файл вместе с функцией обработчика изменений uploadImageDisplay в качестве значения.

<input 
    type="file"
    id="file"
    ref={fileUploadRef}
    onChange={uploadImageDisplay}
    hidden />

Теперь давайте определим функцию uploadImageDisplay.

const uploadImageDisplay = async () => {
    const uploadedFile = fileUploadRef.current.files[0];
    const cachedURL = URL.createObjectURL(uploadedFile);
    setAvatarURL(cachedURL);
}

Здесь происходит несколько вещей:

  • Мы читаем информацию о загруженном файле из свойства files[0]. Массив files содержит информацию обо всех файлах, выбранных с помощью средства выбора файлов. Поскольку мы поддерживаем (или, скорее, обеспокоены) только одним файлом изображения одновременно, мы можем предположить, что первый элемент массива файлов предоставит нам эту информацию.
  • Далее мы используем URL.createObjectURL, чтобы получить кешированный путь к загруженному файлу.
  • Наконец, мы устанавливаем этот путь к файлу в качестве значения переменной состояния, которую мы поддерживаем, чтобы отображать загруженное изображение по умолчанию.

Предварительный просмотр загруженного изображения из кеша браузера

Теперь, если вы выберете изображение в средстве выбора файлов и нажмете кнопку Open внизу,

Вы увидите, что изображение было загружено и сразу же просмотрено.

Отлично! Но было бы даже здорово, если бы мы могли загрузить это изображение на сервер и получить в ответ ссылку для его предварительного просмотра.

Загрузка изображения на сервер

Я нашел этот общедоступный API, который поддерживает загрузку файлов на сервер. Итак, теперь мы выполним POST-вызов этого API с выбранным файлом в качестве полезной нагрузки. Давайте изменим функцию uploadImageDisplay, чтобы это произошло.

const uploadImageDisplay = async () => {
    try {
      setAvatarURL(UploadingAnimation);
      const uploadedFile = fileUploadRef.current.files[0];
      // const cachedURL = URL.createObjectURL(uploadedFile);
      // setAvatarURL(cachedURL);

      const formData = new FormData();
      formData.append("file", uploadedFile);

      const response = await fetch("https://api.escuelajs.co/api/v1/files/upload", {
        method: "post",
        body: formData
      });

      if (response.status === 201) {
        const data = await response.json();
        setAvatarURL(data?.location);
      }

    } catch(error) {
      console.error(error);
      setAvatarURL(DefaultImage);
    }
}

Давайте разберемся с функцией выше:

  • Первоначально мы устанавливаем значение состояния avatarURL с загружаемым изображением.
  • Мы закомментировали код, чтобы показать изображение из кеша браузера.
  • Мы создали данные формы с файлом ключа, как этого ожидает API.
  • Затем мы использовали веб-API выборки для выполнения POST-вызова с данными формы в качестве полезной нагрузки.
  • Получив ответ, мы проверяем, равен ли код статуса 201, что означает, что файл успешно загружен. Мы читаем данные ответа и получаем URL-адрес загруженного изображения из data?.location. Мы устанавливаем это как обновленное значение состояния. Следовательно, загружаемое изображение будет заменено загруженным изображением.
  • Если возникает ошибка, мы устанавливаем значение состояния на изображение по умолчанию.

Поздравляю!!! Вы сделали это.

Вот полный код компонента ImageUpload:

import React, {useState, useRef} from 'react'
import DefaultImage from "../assets/upload-photo-here.png";
import EditIcon from "../assets/edit.svg";
import UploadingAnimation from "../assets/uploading.gif";

const ImageUpload = () => {
  const [avatarURL, setAvatarURL] = useState(DefaultImage);

  const fileUploadRef = useRef();

  const handleImageUpload = (event) => {
    event.preventDefault();
    fileUploadRef.current.click();
  }

  const uploadImageDisplay = async () => {
    try {
      setAvatarURL(UploadingAnimation);
      const uploadedFile = fileUploadRef.current.files[0];
      // const cachedURL = URL.createObjectURL(uploadedFile);
      // setAvatarURL(cachedURL);
      const formData = new FormData();
      formData.append("file", uploadedFile);

      const response = await fetch("https://api.escuelajs.co/api/v1/files/upload", {
        method: "post",
        body: formData
      });

      if (response.status === 201) {
        const data = await response.json();
        setAvatarURL(data?.location);
      }
    } catch(error) {
      console.error(error);
      setAvatarURL(DefaultImage);
    }
  }

  return (
    <div className="relative h-96 w-96 m-8">
      <img 
        src={avatarURL}
        alt ="Avatar"
        className="h-96 w-96 rounded-full" />

      <form id="form" encType='multipart/form-data'>
        <button
          type='submit'
          onClick={handleImageUpload}
          className='flex-center absolute bottom-12 right-14 h-9 w-9 rounded-full'>
          <img
            src={EditIcon}
            alt="Edit"
            className='object-cover' />
        </button>
        <input 
          type="file"
          id="file"
          ref={fileUploadRef}
          onChange={uploadImageDisplay}
          hidden />
      </form>  
    </div>
  )
}

export default ImageUpload;

Пара задач для вас!

Надеюсь, вам понравилась эта статья и она помогла вам понять концепцию. Весь исходный код, использованный в этой статье, можно найти на моем GitHub.

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

  • В настоящее время средство выбора файлов принимает все типы файлов. Можете ли вы ограничить его приемом только файлов типа изображения, например .png, .jpg, .svg и т. д.?
  • Прямо сейчас окно выбора файла появляется, когда мы нажимаем значок редактирования. Можете ли вы внести изменения в код, чтобы он появлялся при нажатии на любое место изображения?

Если вам случится это сделать, пожалуйста, дайте мне знать. Мои соцсети указаны ниже. Вы также можете присоединиться к discord TapaScript, где мы учимся и делимся вещами.

Хотели бы вы добавить в ваш проект поддержку сенсорных жестов? В нашей статье вы узнаете как создать адаптивные и интеракиивные слайдеры.

Я делюсь знаниями о веб-разработке, создании контента, открытом исходном коде и карьере на этих платформах.

Источник:

#JavaScript #React
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу