Image date 다루기(data 변환에 관하여)
Intro
이번에 카메라 앱에서 이미지를 crop하는 기능을 만들려고 canvas를 사용했다. canvas에서 이미지를 사용하려고 하니 input에서 받은 파일의 데이터 형식으로는 canvas에서 이미지를 바로 그릴 수 없다는 것을 알게 되었다.
우리가 웹을 만들면서 바로 받는 타입은 [URL, File] 두 가지이다.
이 두타입에 대해 간략하게 이야기하자면..
URL : 일반적으로 우리가 이미 알고 있거나 외부의 엔드포인트로부터 받은 이미지의 주소의 값을 말한다.
File : Input type file을 사용하면 Input의 event.target.value로 들어오는 값이다.
Image data map
canvas의 이미지에 대해서 이해를 잘하기 위하여 구글링을 해보았더니 다음과 같은 관계도가 있었다.
(출처 : https://observablehq.com/@ehouais/how-to-get-image-data-from-an-url-or-a-file)
이미지 데이터 타입 통일
canvas에 이미지를 그릴 때도 image.src를 이용하고, canvas에서도 이미지를 수정하고 변환을 할 때 toDataURL 그리고 img에서도 src를 사용하기에 이미지는 url로 변환해서 사용하는 것이 가장 편안하다고 생각해서 파일 데이터가 들어오면 이미지 데이터를 url로 통일하도록 하겠다.
File 타입 변환하기
우선 파일이 한 개가 input에 들어온다고 가정하고 코드를 작성하겠다.
function handleFileChange(event: React.ChangeEvent<HTMLInputElement>){
const file = event.target.files?.[0];
//...
};
위의 그래프에는 써있는 방법은 아니지만, File로 파일이 들어오면 다음과 같은 방법으로 base64 데이터타입으로 변환할 수 있다.
function fileToBase64String(file: File) {
return new Promise<string>((res, rej) => {
const reader = new FileReader();
reader.onload = () => {
if (typeof reader.result === 'string') res(reader.result || '');
};
reader.readAsDataURL(file);
reader.onerror = () => '';
});
}
function handleFileChange(event: React.ChangeEvent<HTMLInputElement>){
//현재 이미지의 데이터 타입은 File
const file = event.target.files?.[0];
if(!file) return
//현재 이미지의 데이터 타입은 base64 string으로 인코딩 되었다.
const base64String = await fileToBase64String(file);
//...
};
우리가 이미지 혹은 canvas에서 바로 사용할 수 있도록 url을 만들어야한다. 이 때 우리는 base64 string에서 url로 바꾸기 위해서 Blob으로 변환을 해야한다.
async function base64StringToBlob(base64String: string) {
const base64 = await fetch(base64String);
return base64.blob();
}
function fileToBase64String(file: File) {
return new Promise<string>((res, rej) => {
const reader = new FileReader();
reader.onload = () => {
if (typeof reader.result === 'string') res(reader.result || '');
};
reader.readAsDataURL(file);
reader.onerror = () => '';
});
}
async function handleFileChange(event: React.ChangeEvent<HTMLInputElement>){
//현재 이미지의 데이터 타입은 File
const file = event.target.files?.[0];
if(!file) return
//현재 이미지의 데이터 타입은 base64
const base64String = await fileToBase64String(file);
//현재 이미지의 데이터 타입은 Blob
const jpgBlob = await base64StringToBlob(base64String);
//...
};
드디어 이미지의 그래프에 있는대로 URL.createObjectURL를 이용하여 데이터 url만들 수 있다. (위의 그래프가 정말 다양한 데이터 타입에 대해 이해를 할 수 있지만, 아마 저 그래프는 한 개의 메서드로 바로 변환할 수 있는 경우만 적어놓은 것 같다)
async function base64StringToBlob(base64String: string) {
const base64 = await fetch(base64String);
return base64.blob();
}
function fileToBase64String(file: File) {
return new Promise<string>((res, rej) => {
const reader = new FileReader();
reader.onload = () => {
if (typeof reader.result === 'string') res(reader.result || '');
};
reader.readAsDataURL(file);
reader.onerror = () => '';
});
}
async function handleFileChange(event: React.ChangeEvent<HTMLInputElement>){
//현재 이미지의 데이터 타입은 File
const file = event.target.files?.[0];
if(!file) return
//현재 이미지의 데이터 타입은 base64
const base64String = await fileToBase64String(file);
//현재 이미지의 데이터 타입은 Blob
const jpgBlob = await base64StringToBlob(base64String);
//현재 이미지의 데이터 타입은 URL
const objectURL = URL.createObjectURL(jpgBlob);
console.log(objectURL)
};
Retro
이전에 contentEditable이 가장 성가신 친구인 줄 알았는데 이미지나 canvas를 다루는 것도 이미지 데이터 타입부터 이미지를 변환하고나서의 화질저하와 같은 성가신 것들이 많았다. 간만에 공부할 것이 많아서 재밌었다.