Javascript/React

React로 이미지 업로드 및 필터 적용하고 출력해보기

kimc 2022. 11. 19. 21:15

 

 

이번 글을 통해 배워갈 내용

  1. 리액트 이미지 업로드
  2. 리액트 이미지 필터
  3. 리액트 이미지 출력

 


 

 

 

일단 리액트 프로젝트를 만든 다음

npx create-react-app my-app 

cd my-app 

npm start

 

기본으로 생성되는 

App.js, App.css

파일만 수정하였습니다

 

state 들과 canvas ref들입니다

  const [rawImages, setRawImages] = useState([])
  const [rawPreviews, setRawPreviews] = useState([])
  const [filteredPreviews, setFilteredPreviews] = useState([])
  const canvasRef = useRef(null)

 

전체 렌더는 아래와 같습니다

  return (
    <div className="App">
      <FileInputView />
      <RawImagesView />
      <FilteredImagesView />
      <MyCanvas />
    </div>
  );

 

파일을 업로드합니다

const FileInputView = ()=>{
  return <input type="file" onChange={handleFileInput} accept=".jpg, .jpeg, .png"></input>
}

업로드한 파일을 핸들링합니다

    const file = e.target.files[0]
    if(!isValidFile(file)){
      alert("is not valid file")
      return
    }
    addRawImages(file)
    addRawPreviews(file)
  }

  const addRawImages = (file) => {
    setRawImages([...rawImages, file])
  }

  const addRawPreviews = (file) => {
    setRawPreviews([...rawPreviews, URL.createObjectURL(file)])
  }


  const isValidFile = (file)=>{
    // todo file validation
    return true
  }

(누수방지) 메모리 관리를 위해서 useEffect도 추가합니다

  useEffect(() => {
    // free memory when ever this component is unmounted
    if(rawPreviews != null){
      return () => rawPreviews.forEach(rp=>URL.revokeObjectURL(rp))
    }
}, [rawPreviews])

 

자 이제 이미지가 추가되면 rawImage들이 보입니다

const RawImagesView = ()=>{
  return <div className='imageCon'>
{  rawPreviews.map((item, idx) => <div key={"raw"+idx}><img src={item} alt="not valid" onClick={(e)=>createFilteredImage(e)}/></div>)}
  </div>
}

 

생성된 rawImage를 클릭하면 필터 이미지가 생성됩니다

const createFilteredImage = async (e) =>{

const image = e.target
  const ctx = canvasRef.current.getContext('2d')

  // filter
  ctx.width = image.width;
  ctx.height = image.height;
  ctx.filter = 'grayscale(1)';
  ctx.drawImage(image, 0, 0, image.width, image.height);

  // save
  const dataURL = canvasRef.current.toDataURL();
  setFilteredPreviews([...filteredPreviews, dataURL])
}

필터 이미지를 보여줍니다

const FilteredImagesView = ()=>{
  return <div className='imageCon'>
{  filteredPreviews.map((item, idx) => <div key={"ftr"+idx}><img src={item} alt="not valid" /></div>)}
  </div>
}

 

 

 

전체 코드는 아래와 같습니다

(시간 관계상 리닝은 하지 않았습니다)

import './App.css';
import { useState,useEffect,useRef } from 'react';
function App() {

  const [rawImages, setRawImages] = useState([])
  const [rawPreviews, setRawPreviews] = useState([])
  const [filteredPreviews, setFilteredPreviews] = useState([])
  const canvasRef = useRef(null)

  const handleFileInput = (e) => {
    const file = e.target.files[0]
    if(!isValidFile(file)){
      alert("is not valid file")
      return
    }
    addRawImages(file)
    addRawPreviews(file)
  }

  const addRawImages = (file) => {
    setRawImages([...rawImages, file])
  }

  const addRawPreviews = (file) => {
    setRawPreviews([...rawPreviews, URL.createObjectURL(file)])
  }


  const isValidFile = (file)=>{
    // todo file validation
    return true
  }


  useEffect(() => {
    // free memory when ever this component is unmounted
    if(rawPreviews != null){
      return () => rawPreviews.forEach(rp=>URL.revokeObjectURL(rp))
    }
}, [rawPreviews])


const createFilteredImage = async (e, imagePath) =>{

const image = e.target
  const ctx = canvasRef.current.getContext('2d')
  // ctx.drawImage(image, 0, 0, image.width * .6, image.height * .6);
  // ctx.drawImage(imagePath, 0,0, )


  // filter
  ctx.width = image.width;
  ctx.height = image.height;
  ctx.filter = 'grayscale(1)';
  ctx.drawImage(image, 0, 0, image.width, image.height);

  // save
  const dataURL = canvasRef.current.toDataURL();
  setFilteredPreviews([...filteredPreviews, dataURL])
}

const FileInputView = ()=>{
  return <input type="file" onChange={handleFileInput} accept=".jpg, .jpeg, .png"></input>
}

const RawImagesView = ()=>{
  return <div className='imageCon'>
{  rawPreviews.map((item, idx) => <div key={"raw"+idx}><img src={item} alt="not valid" onClick={(e,item)=>createFilteredImage(e,item)}/></div>)}
  </div>
}

const FilteredImagesView = ()=>{
  return <div className='imageCon'>
{  filteredPreviews.map((item, idx) => <div key={"ftr"+idx}><img src={item} alt="not valid" /></div>)}
  </div>
}

const MyCanvas = ()=>{
  return <canvas ref={canvasRef} style={{display:'none'}}></canvas>
}


  return (
    <div className="App">
      <FileInputView />
      <RawImagesView />
      <FilteredImagesView />
      <MyCanvas />
    </div>
  );
}

export default App;

// https://img.ly/blog/how-to-manipulate-an-image-with-jimp-in-react/
// https://stackoverflow.com/questions/66254514/filter-an-image-on-upload-in-react
// https://www.npmjs.com/package/jimp
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/filter
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL
// https://stackoverflow.com/questions/6850276/how-to-convert-dataurl-to-file-object-in-javascript

 

추가한 CSS

.imageCon{
  margin-top: 200px;
  width: 100%;
  display: grid;
  height: auto;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
}

 

추후에는 JIMP 라이브러리를 사용해서 간편하게 작업해보겠습니다

 

 

 

참조 및 인용

 

How To Manipulate an Image With Jimp in React

Let's get started with Jimp in React. Easily alter images with a few lines of code.

img.ly

 

Filter an Image on upload in react

I am looking to upload an image to my React site, where on upload I can add some filters like grayscale, saturation etc. I can't find anything except https://cloudinary.com/ which won't work due to...

stackoverflow.com

 

jimp

An image processing library written entirely in JavaScript (i.e. zero external or native dependencies). Latest version: 0.16.2, last published: 2 months ago. Start using jimp in your project by running `npm i jimp`. There are 1862 other projects in the npm

www.npmjs.com

 

CanvasRenderingContext2D.filter - Web APIs | MDN

The CanvasRenderingContext2D.filter property of the Canvas 2D API provides filter effects such as blurring and grayscaling. It is similar to the CSS filter property and accepts the same values.

developer.mozilla.org

 

HTMLCanvasElement.toDataURL() - Web APIs | MDN

The HTMLCanvasElement.toDataURL() method returns a data URL containing a representation of the image in the format specified by the type parameter.

developer.mozilla.org

 

How to convert dataURL to file object in javascript?

I need to convert a dataURL to a File object in Javascript in order to send it over using AJAX. Is it possible? If yes, please tell me how.

stackoverflow.com

 

 

 


https://codemasterkimc.tistory.com/50

 

300년차 개발자의 좋은 코드 5계명 (Clean Code)

이번 글을 통해 배워갈 내용  좋은 코드(Clean Code)를 작성하기 위해 개발자로서 생각해볼 5가지 요소를 알아보겠습니다. 개요 좋은 코드란 무엇일까요? 저는 자원이 한정적인 컴퓨터 세상에서 좋

codemasterkimc.tistory.com

 


 

728x90