blob 형태의 자료를 input 태그에 넣기

yojeongjin·2023년 1월 29일
1
post-thumbnail

Vue로 만든 프로젝트를 리팩토링 중 만난 오류 아닌 오류
기존의 사이트에서는

<div class="tbPhoto">
    <input type="file" accept="image/*" ref="image" @change="onChangeFiles" />
    <img v-if="boardUrl" :src="boardUrl" class="tbPhoto-preview" />
</div>

// ... methods
onChangeFiles(e) {
  const file = e.target.files[0]
  this.boardUrl = URL.createObjectURL(file)
  this.boardImg = file
  this.msg= ''
},
  ...

이렇게 @chage 이벤트를 활용해 파일 정보를 받아와 미리보기 형태로 출력되도록 코드를 구성했다.
그 후 폼 데이터를 생성하여 생성한 폼 데이터에 파일 객체를 할당하고 서버에 요청을 보내 백엔드단에서 요청받은 파일을 s3 버킷에 저장하고 DB에 버킷의 파일 경로를 저장 할 수 있게 하였다.

💡여기서 중요한 점은 input 태그의 typefile이 되어야한다는 점이다!

나는 몇몇 사용자들의(그래봐야 칭구칭긔들) 피드백으로 업로드 이미지 사이즈를 수정할 수 있는 창을 새로 만들고있었는데 그 부분은 canvas 태그를 이용해서 여차저차 만들었지만 이 이미지를 다시 저장해서 파일화하여 input 태그에 넣는 작업이 시간이 꽤 걸렸다.

(이렇게 원하는 비율을 설정하거나 업로드하고 싶은 부분만 크롭해서 재업로드가 가능하도록 리팩토링!)

왜냐하면 나는 canvas 위에 이미지를 그린것이고 이 이미지를 input 태그에 넣어 다시 서버로 보내야했기 때문!


처음으로 내가 시도한것은 toDataURL() 함수를 통해 캔버스에 그린 그림을 문자열 형태로 변환하여 input 태그에 넣는것이였다.
이것은 canvas에 그려진 내용을 Data URL로 변환한 것이기 때문에 그 자체로 이미지 정보가 된다. 따라서 이 문자열을 이미지 객체에 바인딩하거나 다른 이미지로 생성할 수 있게 된다.
즉, 문자열로 변환하여 img 요소의 src 로 사용하는것 정도는 가능하다!

그러나 이것은 base 64로 인코딩된 data url 형식의 문자열을 반환하였기때문에
base 64 디코딩 -> Blob 객체 생성 -> Blob -> File로 변환 과정을 거쳐야했다.

따라서,,,

save() {
 // toDataURL()사용하여 webp타입의 base64인코딩된 data url 형식의 문자열을 반환
const url = this.modiCanvas.toDataURL("image/webp")

//img의 src로 사용될 부분
this.editUrl = url
  
  // atob = ASCII -> binary
  // btoa = binary -> ASCII
  // base64 데이터 디코딩
const decodImg = atob(url.split(',')[1])
let array = []
 // i 에 해당하는 string을 unicode로 변환
for (let i=0; i<decodImg.length; i++) {
  array.push(decodImg .charCodeAt(i))
}
  // console.log(array)
  // (2486) [137, 80, 78, 71, ...]
  // Blob 생성

이 부분까지는 어찌어찌 구현을 해냈다. 문제는 이것을 file로 변환하는 일이였다.
아무리 구글링을해봐도 검색어의 문제인지 내가 찾던 부분은 나오지않았고 ㅠㅠ
그렇게 몇시간을 날리고 문득 든 생각이 Blob을 상속 받은 것이 File 아닌가? 싶어서 이 부분에 대해서 다시 찾아보니 ..

  • File: Blob을 상속 받는 객체로, 주로 "파일 형태"의 바이너리 데이터(mp3, png 파일 등)를 다룬다.

💡 오호!! 애초에 처음부터 File로 만들어서 input태그에 넣으면 되겠네!!!!!!!!!!


const url = this.modiCanvas.toDataURL("image/webp")
this.editUrl = url

const fileName = 'canvas_img_' + new Date().getMilliseconds() + '.webp'

const decodImg = atob(url.split(',')[1])
let array = []
for (let i=0; i<decodImg.length; i++) {
  array.push(decodImg .charCodeAt(i))
}

const file = new File([new Uint8Array(array)], fileName, {type: 'image/webp'})

이렇게 File 객체를 새로 만들어서 formData에 넣어서 해결하였다!


이 부분이 막힌 덕분에 blob과 file에 대해서 다시 한번 공부 할 수 있었고,
역시나 머리로 알고있는것과 한번 구현해보는것의 엄청난 차이를 다시 한번 느낄 수 있었다.

profile
IT is my race🐢

0개의 댓글