문제상황
아트워크(상품을 만들기위한 이미지)를 업로드 시킬 때, 이미지 사이즈를 maxwidth or height을 800으로 맞추다보니 용량이 크거나 이미지 사이즈가 컸던 이미지가 깨지는 현상 발견
해결방법 고안
(1) 이미지 width나 height를 바로 바꾸는 것이 아닌 height나 width 중 큰 부분을 기준으로 삼아 해당 길이가 800될 때까지 반으로 쪼갠다.
(2) 800이 된 길이의 비율에 맞춰서 나머지 한 쪽의 길이를 수정한다.
(3) 해당 길이로 이미지의 사이즈를 바꾸는데, 이미지 해상도를 해치지 않고 줄이는 패키지를 사용해 이미지를 줄인다.
(4) 이미지 태그로 만들어놓은 형식을 파일형식으로 만들어 api에 담아 보낸다.
기존 코드
const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
const { files } = e.target;
if (!files) return;
const [file] = Array.from(files);
if (!file) return;
// if (file.size > 10485760 || !(file.type.includes('jpg') || file.type.includes('jpeg') || file.type.includes('png') || file.type.includes('svg'))) {
if (!(file.type.includes('jpg') || file.type.includes('jpeg') || file.type.includes('png') || file.type.includes('svg'))) {
Toast.show(t('selleb_upload:$MODAL_MESSAGE.FAIL.NO_FILE_FORM'));
} else {
let originalFile: undefined | File = file;
if (!file.type.includes('svg')) { // exif의 Orientation을 반영한 이미지를 얻는다.
const originalOptions = {
useWebWorker: true
};
originalFile = await imageCompression(file, originalOptions);
}
const options = {
maxSizeMB: 3,
maxWidthOrHeight: 800,
useWebWorker: true
};
const compressedFile = await imageCompression(newFile, options);
const api = api
try {
const response = await api.upload(originalFile as RcFile,newCompressedFile);
setSavedArtworkIndex(response.artworkIndex);
alertModalStore1.setVisible(true);
alertModalStore1.setMessage(
<p>{t('selleb_upload:$MODAL_MESSAGE.SUCCESS.UPLOAD_IMG')}</p>
);
if (Toast.showResponse(response, t('아트워크 이미지가 등록되었습니다.'))) {
handleShowEditor(response.artworkIndex);
}
} catch (error) {
Toast.showError(error, t('selleb_upload:$MODAL_MESSAGE.FAIL.NOT_UPLOAD'));
}
}
}
};
⇒ 기존 코드에서는 아트워크의 이미지 사이즈를 바로 변경하여, 이미지 화질 저하의 이슈가 있다.
⇒ 또한, 기존에 사용한 imageCompression 패키지가 화질 저하를 방지하는 코드가 없는 것 같다.
(1) 업로드 함수
const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
const { files } = e.target;
if (!files) return;
const [file] = Array.from(files);
if (!file) return;
// if (file.size > 10485760 || !(file.type.includes('jpg') || file.type.includes('jpeg') || file.type.includes('png') || file.type.includes('svg'))) {
if (!(file.type.includes('jpg') || file.type.includes('jpeg') || file.type.includes('png') || file.type.includes('svg'))) {
Toast.show(t('selleb_upload:$MODAL_MESSAGE.FAIL.NO_FILE_FORM'));
} else {
let originalFile: undefined | File = file;
//이미지 리사이징_이미지 사이즈 확인용 이미지 생성
let img = new Image();
img.src = window?.URL.createObjectURL(file);
img.onerror = function () {
//에러 토스트 (비정상적인 파일입니다.)
Toast.show(t('selleb_upload:$MODAL_MESSAGE.FAIL.NO_NORMAL_FILE'));
};
img.onload = async function () {
//이미지 리사이징_이미지 사이즈 가로나 세로 중 긴거로 scale 변경
const imgScaleSize = scaleIt(0.5, img?.width, img?.height);
//이미지 리사이징 라이브러리 사용하여 이미지 자체 사이즈 변경
const changedScaleImage = await Morpheus?.resize(file, {
height: imgScaleSize?.resizeHeight,
width: imgScaleSize?.resizeWidth
});
const newCompressedFile : File | undefined = makeNewFileForm(changedScaleImage, file);
if (!file.type.includes('svg')) {
const changedOriginalImage = await Morpheus?.resize(file, {
height: img?.height,
width: img?.width
});
originalFile = makeNewFileForm(changedOriginalImage, file);
}
const api = api
try {
const response = await api.upload(originalFile as RcFile, file.name, newCompressedFile);
setSavedArtworkIndex(response.artworkIndex);
alertModalStore1.setVisible(true);
alertModalStore1.setMessage(
<p>{t('selleb_upload:$MODAL_MESSAGE.SUCCESS.UPLOAD_IMG')}</p>
);
// if (Toast.showResponse(response, t('아트워크 이미지가 등록되었습니다.'))) {
// handleShowEditor(response.artworkIndex);
// }
} catch (error) {
Toast.showError(error, t('selleb_upload:$MODAL_MESSAGE.FAIL.NOT_UPLOAD'));
}
}
}
};
(2) 이미지 사이즈 점진적 축소 함수
//스케일 줄이기(반씩 줄임)
const scaleIt = (scaleFactor: number, width: number, height: number) => {
const MAX_SIZE = 800;
let resizeWidth = width;
let resizeHeight = height;
if (resizeWidth > resizeHeight) {
while (resizeWidth > MAX_SIZE) {
resizeWidth *= scaleFactor;
if (resizeWidth < MAX_SIZE){
resizeWidth = MAX_SIZE;
resizeHeight *= MAX_SIZE / width;
break;
}
}
} else {
while (resizeHeight > MAX_SIZE) {
resizeHeight *= scaleFactor;
if (resizeHeight < MAX_SIZE){
resizeHeight = MAX_SIZE;
resizeWidth *= MAX_SIZE / height;
break;
}
}
}
return { resizeWidth, resizeHeight };
};
(3) canvas파일을 data형식으로 변환하는 함수를 만들어 이미지를 파일로 변환한다.
//canvas파일을 data형식으로 변환
const makeNewFileForm = (imgCanvas: HTMLCanvasElement, originalFile: File | undefined) => {
if (!imgCanvas.toDataURL) return;
let type : string;
if (originalFile?.type === 'svg') {
type = 'image/jpeg';
} else {
type = originalFile?.type || 'image/jpeg';
}
const dataURL = imgCanvas?.toDataURL(type, 1);
const byteString = window.atob(dataURL?.split(',')[1]);
const mimeString = dataURL?.split(',')[0].split(':')[1].split(';')[0];
const ab = new ArrayBuffer(byteString.length);
const ia = new Uint8Array(ab);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return (new File([ab], originalFile?.name, { type: mimeString }))
};