[react] input (file)에 같은 파일 다시 올리기 (+image preview)

Ell!·2021년 12월 10일
3

react

목록 보기
15/28

file upload

자바스크립트에서 file을 upload 하기 위해서는 input 태그를 type="file"로 해서 사용한다.

이때 file을 upload하면 onChange event가 발생하는데, 같은 파일을 다시 upload할 경우 event가 trigger되지 않는다. (onChange는 실질적인 내용 변화에만 trigger되기 때문!)
따라서 file이 upload되면 값을 초기화해주어야 한다. 내용물을 담고 있는 e.target.value를 초기화해주자!

React에서 구현

input

// react-hook-form을 함께 사용

              <Controller
                name="profileImage"
                control={control}
                render={({ field }) => {
                  return (
                    <input
                      accept="image/*"
                      id="profile-image-input" // label htmlFor
                      type="file" // type="file"
                      hidden // input을 숨기고 다른 스타일링위해
                      onChange={e => {
                        handleProfileImageChange(e); 
                      }}
                    />
                  );
                }}
              />

              <label htmlFor="profile-image-input" className="photo-icon">
                <IconButton variant="outlined" color="primary" component="span">
                  <AddAPhotoIcon />
                </IconButton>
              </label>
  • inputonChange부분을 보면 다음 함수를 실행중이다.
    handleProfileImageChange(e)
    실질적으로 image를 처리하고 preview 시켜주는 함수. 후술.

onChange function


// image preview위한 state
const [loadedProfileImage, setLoadedProfileImage] = useState({
    imagePreviewUrl: '',
    imageBlob: null,
  });

  let reader = new FileReader(); // FileReader API로 이미지 인식
  const handleProfileImageChange = e => {
    e.preventDefault();

    const file = e.target.files[0]; // file object는 e.target.files[0]에 있다.

    if (file) {
      reader.readAsDataURL(file); // 1. reader에게 file을 먼저 읽히고
      // 사진 올리고 나서 처리하는 event
      reader.onloadend = () => {
        setLoadedProfileImage({ imagePreviewUrl: reader.result });
        dispatch(triggerImageCropModal()); // 사진 업로드 하면 crop창 띄움
        e.target.value = ''; // 💖 같은 파일을 올리면 인지못해서 여기서 초기화
      }; // 2. 비동기적으로 load가 끝나면 state에 저장
    }
  };

file 객체는 위와 같이 구성되어있다.

이 file 객체를 reader.readAsDataURL을 통해 읽는다. MDN에 따르면

readAsDataURL 메서드는 컨텐츠를 특정 Blob 이나 File에서 읽어 오는 역할을 합니다. 읽어오는 read 행위가 종료되는 경우에, readyState (en-US) 의 상태가 DONE이 되며, loadend (en-US) 이벤트가 트리거 됩니다. 이와 함께, base64 인코딩 된 스트링 데이터가 result 속성(attribute)에 담아지게 됩니다.

이제 img 태그의 src에 loadedProfileImage.imagePreviewUrl을 담으면 preview로 볼 수 있다.

참고

https://wiki.jjagu.com/?p=371

profile
더 나은 서비스를 고민하는 프론트엔드 개발자.

0개의 댓글