Front-End/JavaScript

[JavaScript] 이미지 파일 리사이징

foobarbaz 2018. 10. 8. 17:57

웹 페이지에서 서버로 이미지 파일을 올려주기 전 이미지의 크기가 클 경우 리사이징 후 파일을 올려주고자 한다.


서버단에서도 조정할 수 있지만 서버에 무리가 올수 있기에 클라이언트단에서 처리하는 것이 좋다고 한다.


아래 두 주소는 코드를 참고한 사이트이다.


https://stackoverflow.com/questions/23945494/use-html5-to-resize-an-image-before-upload


https://codepen.io/tuanitpro/pen/wJZJbp



하단의 코드는 전체 소스코드이다.

이 코드는 파일이 resizing 됐는지 확인하기 위한 코드이며 메모장으로 .html로 저장하여 간단하게 확인할 수 있도록 upload부분은 제외하였다.

(file 크기확인은 f12 개발자모드 콘솔을 참조하면 된다.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
 
    <input type="file" name="file_name" id="file_name" multiple="multiple" />
    <button type="button" id="upload" >upload</button>
    <img src="" id="preview"  >
    <img src="" id="output">
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script>
var resizeImage = function (settings) {
    var file = settings.file;
    var maxSize = settings.maxSize;
    var reader = new FileReader();
    var image = new Image();
    var canvas = document.createElement('canvas');
    var dataURItoBlob = function (dataURI) {
        var bytes = dataURI.split(',')[0].indexOf('base64'>= 0 ?
            atob(dataURI.split(',')[1]) :
            unescape(dataURI.split(',')[1]);
        var mime = dataURI.split(',')[0].split(':')[1].split(';')[0];
        var max = bytes.length;
        var ia = new Uint8Array(max);
        for (var i = 0; i < max; i++)
            ia[i] = bytes.charCodeAt(i);
        return new Blob([ia], { type: 'image/jpeg'});
    };
    var resize = function () {
        var width = image.width;
        var height = image.height;
        if (width > height) {
            if (width > maxSize) {
                height *= maxSize / width;
                width = maxSize;
            }
        } else {
            if (height > maxSize) {
                width *= maxSize / height;
                height = maxSize;
            }
        }
        canvas.width = width;
         canvas.height = height;
        canvas.getContext('2d').drawImage(image, 00, width, height);
        var dataUrl = canvas.toDataURL('image/jpeg');
        return dataURItoBlob(dataUrl);
    };
    return new Promise(function (ok, no) {
        if (!file.type.match(/image.*/)) {
            no(new Error("Not an image"));
            return;
        }
        reader.onload = function (readerEvent) {
            image.onload = function () { return ok(resize()); };
            image.src = readerEvent.target.result;
        };
        reader.readAsDataURL(file);
    });
};
 
let fileMap = new Map();
 
$(document).ready(function() {
    $("#file_name").on("change", select);
    $("#upload").off("click").on("click", upload);
});
 
function select(){
    $.each(this.files, function(index, file){
        var reader = new FileReader();
        reader.onload = function(e){
            document.getElementById('preview').src = e.target.result;
        };
        reader.readAsDataURL(file);
        
        // resizing 이전 파일
        fileMap.set("1_"+file.name,file);
        resizeImage({
            file: file,
            maxSize: 500
        }).then(function (resizedImage) {
            reader.onload = function(e){
                document.getElementById('output').src = resizedImage;
            };
            reader.readAsDataURL(file);
 
            // resizing 이후 파일
            fileMap.set("2_"+file.name,resizedImage);
        });
    });
    
}
 
function upload(){
    console.log("fileMap : " + fileMap.size);
    console.log(fileMap);
}
</script>
</body>
</html>
cs

설명

위 코드는 ES6에서 추가된 Promise와 Map을 활용하였다.

HTML


1
2
3
4
5
    <input type="file" name="file_name" id="file_name" multiple="multiple" />
    <button type="button" id="upload" >upload</button>
    <img src="" id="preview"  >
    <img src="" id="output">
    <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
cs


<script>


작동은 


1. 파일 선택시 resizing 전과 후의 파일을 Map에 저장한다.


2. upload 파일을 보낸다. (이 코드는 console.log()로 확인을 위한 용도만 작성되어있다.)


1
2
3
4
$(document).ready(function() {
    $("#file_name").on("change", select);
    $("#upload").off("click").on("click", upload);
});
cs



ES6에서 추가된 Map이다.


다른 언어에서 사용하는 HashMap 과 동일하게 사용된다.


1
let fileMap = new Map();
cs


아래 소스코드는 이미지 파일을 resizing하는 함수다. 


settings에는 file과 maxSize 초기값을 추가해 함수로 보내준다. 


dataURItoBlob : dataURI를 blob형태로 변환하기 위한 함수이다.


resize : 이미지 파일을 지정한 maxSize로 size를 조절해준다.


return new Promise(function (ok, no) { }; : ES6에서 추가된 Promise로 작업을 비동기 적으로 실행시키는 함수이다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
var resizeImage = function (settings) {
    var file = settings.file;
    var maxSize = settings.maxSize;
    var reader = new FileReader();
    var image = new Image();
    var canvas = document.createElement('canvas');
    var dataURItoBlob = function (dataURI) {
        var bytes = dataURI.split(',')[0].indexOf('base64'>= 0 ?
            atob(dataURI.split(',')[1]) :
            unescape(dataURI.split(',')[1]);
        var mime = dataURI.split(',')[0].split(':')[1].split(';')[0];
        var max = bytes.length;
        var ia = new Uint8Array(max);
        for (var i = 0; i < max; i++)
            ia[i] = bytes.charCodeAt(i);
        return new Blob([ia], { type: 'image/jpeg'});
    };
    var resize = function () {
        var width = image.width;
        var height = image.height;
        if (width > height) {
            if (width > maxSize) {
                height *= maxSize / width;
                width = maxSize;
            }
        } else {
            if (height > maxSize) {
                width *= maxSize / height;
                height = maxSize;
            }
        }
        canvas.width = width;
        canvas.height = height;
        canvas.getContext('2d').drawImage(image, 00, width, height);
        var dataUrl = canvas.toDataURL('image/jpeg');
        return dataURItoBlob(dataUrl);
    };
    return new Promise(function (ok, no) {
        if (!file.type.match(/image.*/)) {
            no(new Error("Not an image"));
            return;
        }
        reader.onload = function (readerEvent) {
            image.onload = function () { return ok(resize()); };
            image.src = readerEvent.target.result;
        };
        reader.readAsDataURL(file);
    });
};
cs


하단의 소스는 선택한 파일을 resizing 이전과 이후 두개의 데이터를 fileMap 에추가한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function select(){
    $.each(this.files, function(index, file){
        var reader = new FileReader();
        reader.onload = function(e){
            document.getElementById('preview').src = e.target.result;
        };
        reader.readAsDataURL(file);
        
        // resizing 이전 파일
        fileMap.set("1_"+file.name,file);
 
        resizeImage({
            file: file,
            maxSize: 500
        }).then(function (resizedImage) {
            reader.onload = function(e){
                document.getElementById('output').src = resizedImage;
            };
            reader.readAsDataURL(file);
 
            // resizing 이후 파일
            fileMap.set("2_"+file.name,resizedImage);
        });
    });
}
cs


하단의 소스는 'preview' 태그에 이미지 src를 변경한다. (즉 이미지를 띄운다.)


var reader = new FileReader();
reader.onload = function(e){
      document.getElementById('preview').src = e.target.result;
};
reader.readAsDataURL(file);


resizeImage() 함수는 file과 maxsize의 초기값을 함수로 넘겨준다.


resizeImage({
      file: file,
      maxSize: 500
})


then은 Promise 사용시 사용하는 함수이다.


간단히 설명하자면 Promise를 사용한 비동기 작업 완료 후 .then()가 실행된다.


주로 chain형태로 연결되어있다. 


.then(function (resizedImage) {
      // Map에 file 저장
      fileMap.set(file.name,resizedImage);
});




추가설명


※ 선택한 파일이 이미지일 경우만 resizing하고 싶다면 아래 조건을 추가


1
2
3
4
// image파일만 가져와서 실행
if(file.type.match(/image.*/)){
    
};
cs


※ 일정 크기 이상만 resizing을 실행하고 싶다면 아래 조건을 추가


1
2
3
if (file.size > 204800){ 
 
};
cs


※ file을 서버로 보내고 싶다면 FormData() 로 저장 후 보내자. (ajax 사용시 data 부분에 formData를 보내주면 된다.)


※ 다른 데이터도 함께 보내고 싶을 경우 serializeObject()를 이용하자.


※ Service 쪽에서는 MultipartHttpServletRequest request를 이용해 사용하도록 하자.


1
2
3
4
5
List<MultipartFile> file_list = request.getFiles("file_name");
 
for(int i=0;i<file_list.size();i++){
            MultipartFile multipartFile = file_list.get(i);
};
cs



※ Blob은 API들 간에 데이터를 교환하기 위해 사용하는 불투명(opaque) 타입이다.


※ ES6는 ECMAScript 6 로 2015년 6월에 발표된 javascript 개정판이다. 




'Front-End > JavaScript' 카테고리의 다른 글

[JavaScript] IE에서 Audio 사용하기  (0) 2019.05.21
[JavaScript] 달력 만들기  (5) 2018.10.08