[Vue.js] JS Canvas요소 이미지로 axios로 서버에 보내기

O_K·2022년 3월 13일
2

Vue.js

목록 보기
1/1
post-thumbnail

개요

프로젝트 중 버튼을 누르면
1. canvas 데이터 생성
2. canvas데이터 blob파일로 변환
3. form data에 담아서 axios 요청으로 보내기
사진파일 생성과 동시에 게시글이 작성되는 로직을 구현하고 싶었다

var file = new Blob([new ArrayBuffer(array)], {type: 'image/png'});

위와 같이 MIME type을 설정을 해주면 된다고 생각했다.
하지만 backend(springboot)로 POST 요청 후, DB를 확인해보면

사진과 같이 .blob url로 저장이 되었다.

비록 프로젝트기간에는 해결하지 못하고 a태그를 사용하여
사진 파일을 다운 받고 수동으로 다시 게시글을 업로드 하는 로직으로 대체하였지만...
.png로 저장이 되도록 하는 목표를 가지고 다시 시도!


Blob이란?

  • Binary Large Object의 약자
  • 바이너리 형태의 큰 객체(Large Object)를 저장
  • 큰 객체 = 멀티미디어 객체(이미지, 비디오, 사운드...)
  • DB에 직접 저장이 아니라 DB에는 큰 객체의 위치포인터만 저장

Blob 생성

const newBlob = new Blob([typedArray], options);

typedArray

Blob 생성자의 첫번째 인수(typedArray)로
ArrayBuffer, ArrayBufferView, Blob(File), USVString(DOMString) 객체 또는 이러한 객체가 혼합된 Array를 사용할 수 있다.
USVString은 UTF-8로 인코딩된다.

new Blob([new ArrayBuffer(data)], { type: 'video/mp4' });
new Blob(new Uint8Array(data), { type: 'image/png' });  
new Blob(['<div>Hello Blob!</div>'], {
  type: 'text/html',
  endings: 'native'
});

options

옵션으로는 typeendings를 설정 가능

  • type
    데이터의 MIME 타입을 설정
    기본값은 ""

  • endings
    데이터가 텍스트일 때, 개행 문자(\n)를 어떻게 해석할지 지정
    "transparent""native"로 지정할 수 있음
    기본값은 "transparent"

    • transparent : 개행 문자를 바꾸지 않고 블롭 데이터로 복사
    • native : 호스트 시스템 컨벤션에 맞춰서 변환
var blob = new Blob([typedArray], {type: 'application/octet-binary'});

const obj = {hello: 'world'};
const blob = new Blob([JSON.stringify(obj, null, 2)], {type : 'application/json'});

MIME란?

MIME(Multipurpose Internet Mail Extension)

  • 이메일과 함께 동봉할 파일을 텍스트 문자로 전환하여 이메일 시스템으로 전달하기 위해 개발됨

예전에는 ACII로 공통 표준따르기만하면 됐지만, 점차 음악 등 binary 파일을 보내야 하는 경우가 생겼고 ASCII로는 전송이 불가능하게 됨
-> 텍스트 파일로의 변환 필요
-> 즉, MIME는 새로운 인코딩 방식

Encoding : binary -> text
Decoding : text -> binary

MIME type이란 클라이언트에게 전송된 문서의 다양성을 알려주기 위한 메커니즘
(웹에서 파일의 확장자는 별 의미가 없다 -> 각 문서와 함께 올바른 MIME 타입을 전송하도록, 서버가 정확히 설정하는 것이 중요)
브라우저들은 리소스를 내려받았을 때 해야 할 기본 동작이 무엇인지를 결정하기 위해 대게 MIME 타입을 사용


canvas data를 Blob 파일로 변환 후, axios POST 요청으로 DB 저장하기

< 기존 코드 >

canvas 데이터 png 파일로 다운받는 로직
1. canvas 데이터 그리기
2. <a>태그를 사용하여 다운로드

    draw() {
      var canvas = document.getElementById("canvas");
      if (canvas.getContext) {
        var ctx = canvas.getContext("2d");
        ctx.font = "20px serif";
        ctx.fillStyle = "rgb(204,229,255)";
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = "rgb(0,0,0)";
        ctx.fillText("Hello", 20, 30);
      }
      this.saveImage();
    },

    saveImage() {
      var link = document.createElement("a");
      link.download = "hello.png";
      link.href = document.getElementById("canvas").toDataURL("image/png");
      link.click();
    },

canvas.toDataURL("image/png")

  • png타입의 base64인코딩된 data url 형식의 문자열을 반환
canvas.toDataURL(type, encoderOptions);
  • canvas에 그린 이미지를 toDataURL() 메서드를 활용하여 base64 문자열로 받을 수 있다
  • 매개변수로 지정된 형식의 이미지 표현을 포함하는 데이터 URItype 를 반환 (원하는 파일 형식과 이미지 품질을 지정 가능)
type (Optional)
  • 이미지 포맷 스트링 입력
  • "image/png"가 디폴트
encoderOptions (Optional)
  • 이미지 퀄리티 지정
  • 0과 1사이 값 입력
반환 값
data:[<type>][;base64],<data>

< 변경 코드 >

canvas 데이터 png 파일로 변환 후 axios 보내기
1. canvas 데이터 그리기
2. toDataURL()로 canvas 데이터 base64 인코딩 문자열 반환
3. base 64 디코딩 -> Blob 객체 생성
4. Blob -> File로 변환
5. FormData() 형태로 axios POST

draw() {
      var canvas = document.getElementById("canvas");
      if (canvas.getContext) {
        var ctx = canvas.getContext("2d");
        ctx.font = "20px serif";
        ctx.fillStyle = "rgb(204,229,255)";
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = "rgb(0,0,0)";
        ctx.fillText("Hello", 20, 30);
      }

      // toDataURL()사용하여 png타입의 base64인코딩된 data url 형식의 문자열을 반환
      var dataUrl = canvas.toDataURL('image/png')
      console.log(dataUrl)

      // data:image/jpeg;base64,/9j/4AAQSkZJRg...AAAAAB//2Q==
      // data : <type> <;base64> <data>
      
      // <data> 부분 뽑아내기
      // atob = ASCII -> binary
      // btoa = binary -> ASCII
  	  // base64 데이터 디코딩
      var byteString = window.atob(dataUrl.split(',')[1]);
      var array = [];
      // i 에 해당하는 string을 unicode로 변환
      for (var i = 0; i < byteString.length; i++) {
          array.push(byteString.charCodeAt(i));
      }
  	  // console.log(array)
  	  // (2486) [137, 80, 78, 71, ...]
  	  // Blob 생성
      var myBlob = new Blob([new ArrayBuffer(array)], {type: 'image/png'});

      // ** Blob -> File 로 변환**
      var file = new File([myBlob], "blobtofile.png");
      var formData = new FormData();
      formData.append("media", file);
      formData.append("content", "Blob확인");
      formData.append("tagList", "blob");
      formData.append("username", "admin");

      axios
        .post("/feed/feeds", formData, {
          headers: {
            "Content-Type": "multipart/form-data",
            Authorization: sessionStorage.getItem("jwt"),
          },
        })
        .then((res) => {
          console.log(res);
        })
        .catch((err) => {
          alert("실패");
          console.log(err)
        });
	},


성공적으로 DB에 저장됨을 볼 수 있다!🥳

예상하기로는 File 자체로 받도록 backend 쪽에서 처리를 한 것 같은데 이 부분 때문에 중간에 Blob을 File 객체로 다시 변환해서 전송시키는 부분이 필요했던 것 같다
덕분에 이런저런 공부가 되었다!


참고자료

https://heropy.blog/2019/02/28/blob/
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=magnking&logNo=220950061851
https://developer.mozilla.org/ko/docs/Web/API/Blob#%EB%B8%94%EB%A1%AD%EC%97%90%EC%84%9C_%EB%8D%B0%EC%9D%B4%ED%84%B0_%EC%B6%94%EC%B6%9C%ED%95%98%EA%B8%B0
https://kyounghwan01.github.io/blog/JS/JSbasic/Blob-url/#createobjecturl
http://charlie0301.blogspot.com/2014/10/html5-canvas-blob-data-post-upload.html
https://stackoverflow.com/questions/18253378/javascript-blob-upload-with-formdata
https://samanoske.tistory.com/m/94
https://taeny.dev/javascript/file-object/#1-file-%EA%B0%9D%EC%B2%B4

profile
즐거운 개발자가 목표

1개의 댓글

comment-user-thumbnail
2022년 3월 13일

프론트 마스터가 되어가시네요 👀

답글 달기