본문으로 건너뛰기

비동기 이미지 업로드 - 리사이징 및 이미지 회전을 포함

· 약 6분

비동기 업로드를 이용하면 멀티 이미지 업로드나 모바일 이미지 업로드를 쉽게 처리할 수 있다.

모바일 이미지 업로드시에는 exif 속성에 따라 (카메라로 찍은 방향에 따라) 이미지가 회전되어서 올라가게 되고, 비율로 이미지를 표시해야 되기 때문에 리사이징이 필요하다.

이 부분은 load-image 모듈을 사용하면 다 해결된다. 아니라면.. 사진 값을 바이너리로 읽어 처리해야한다.

설치

# bower install https://github.com/blueimp/JavaScript-Load-Image.git --save
$ yarn add blueimp-load-image

소스

<script type="text/javascript">
var imageModule = (function () {
"use strict";
var possible = window.File && window.FileReader && window.FormData; // html5 업로드를 지원하는지의 여부
var apiPath = "/api/test.php"; // 업로드 서버 처리 경로
var folder = "/upload/review/"; // 기본 이미지 업로드 폴더
var $input;
var maxWidth = 600;
var maxHeight = 480;
var imageArray = []; // callback에 사용할 이미지 파일명 배열
var callback;
/**
* [sendImageFile 이미지 비동기 업로드]
* @scope {[private]}
* @param {[string]} imageData [이미지 binary]
* @return {[function]} callback [업로드 후처리]
*/
function sendImageFile(imageData) {
if (imageData) {
var formData = new FormData();
formData.append("type", "upload");
formData.append("folder", folder);
formData.append("imageData", imageData);
$.ajax({
type: "post",
url: apiPath,
data: formData,
dataType: "json",
contentType: false, // contentType header 제거
processData: false, // Dom 객체를 전송하려면 false 처리해야함
})
.then(function (data) {
if (data.data) {
imageArray.push(data.data.file); // 이미지 배열에 저장
$input.val("");
if (callback) {
callback(imageArray);
} else {
console.log("callback function not initialized");
}
}
})
.catch(function (error) {
console.log("upload fail: ", error);
});
} else {
console.log("image data is null");
}
}
return {
/**
* [init 이미지 모듈 초기화]
* @param {[string]} fileId [변경할 input id]
* @param {[function]} callbackFunc [후처리]
* @param {[object]} settings [width, height, folder 설정가능]
*/
init: function (fileId, callbackFunc, settings) {
if (possible) {
if (typeof settings === "object") {
if (settings.width) {
maxWidth = settings.width;
}
if (settings.height) {
maxHeight = settings.height;
}
if (settings.folder) {
folder = settings.folder;
}
}
if ($.isFunction(callbackFunc)) {
callback = callbackFunc;
} else {
console.log("callback is not defined");
}
var options = {
maxWidth: maxWidth, // resize width 값
maxHeight: maxHeight, // resize height 값
canvas: true, // 사진을 돌리려면 canvas 객체로 받아야함
downsamplingRatio: 0.5, // 비율에 맞추어 크기 감소
};
$input = $("#" + fileId);
$input.on("change", function (e) {
e.preventDefault();
e = e.originalEvent;
var target = e.dataTransfer || e.target;
var file = target && target.files && target.files[0];
loadImage.parseMetaData(file, function (data) {
// exif js를 사용해 이미지의 tag를 가져옴
if (data.exif) {
options.orientation = data.exif.get("Orientation"); // 화면이 돌아간 비율을 지정
} else {
if (options.orientation) {
options.orientation = null;
}
}
// 캔버스 이미지 리사이징 처리 후 서버호출
loadImage(
file,
function (img) {
sendImageFile(img.toDataURL());
},
options,
);
});
});
} else {
console.log("file upload is not supported");
}
},
/**
* [getFolder 업로드 경로 호출]
* @return {[string]} [경로]
*/
getFolder: function () {
return folder;
},
/**
* [delete 이미지 삭제]
* @param {[number]} idx [이미지 배열의 인덱스]
* @param {[function]} callbackFunc [delete 후처리]
*/
delete: function (idx, callbackFunc) {
if (imageArray[idx]) {
$.ajax({
url: apiPath,
type: "post",
data: {
type: "delete",
folder: folder,
filename: imageArray[idx],
},
dataType: "json",
})
.then(function (data) {
console.log("file delete success");
console.log(data);
if ($.isFunction(callbackFunc)) {
callbackFunc(data);
}
})
.catch(function (error) {
console.log("server file delete failed");
console.log(error);
})
.done(function () {
imageArray.splice(idx, 1);
$input.val("");
if (callback) {
callback(imageArray);
} else {
console.log("callback function not initialized");
}
});
} else {
console.log("delete index not defined");
}
},
clear: function () {
console.log("clear array");
imageArray = [];
},
};
})();
$(function () {
imageModule.init(
"fileInput",
function (data) {
var folder = imageModule.getFolder();
var str = "";
for (var i = 0, len = data.length; i < len; i++) {
str +=
'<img src="' +
folder +
data[i] +
'" style="width:100%;height:100%;">';
}
$("#area").html(str);
$("#fileData").val(JSON.stringify(data));
},
{},
);
// options, width height folder..
// {width:500, height:300, folder:'/upload/board/'}
});
// after insert into database
$("#area").empty();
$("#fileData").val("");
imageModule.clear();
</script>

<input type="file" name="imageFile" id="fileInput" />

설명

코드양이 되게 긴데, 5-6 줄에서 업로드 서버 처리 경로 및 기본 업로드 폴더를 지정해준다. 9-10 줄에서는 최대 가로 세로(px)을 지정해준다. 비율로 줄어들기 때문에 걱정하지말고 지정하자

사용법은 169 줄 이후를 보면 된다. callback 함수를 통해 올라간 이미지를 바로 보여주게 처리할 수 있다. 184 줄부터는 db 와의 통신이 끝난 후 화면을 초기화해주는 부분이다.

127 줄의 imageModule.delete 함수를 사용해 업로드 된 사진을 삭제할 수 있다.

현재는 api/test.php로 서버의 업로드 처리 로직을 구현해놨는데, type 을 받아 delete 일 경우 해당 이미지를 삭제, 아닐 경우 binary 데이터를 이미지로 변환해주게 로직을 짜면 된다.