Programing/JavaScript

javascript 동적 멀티파일 업로드, 동적으로 선택된 파일 전달, ajaxForm 활용 fileList 컨트롤, dynamic file upload

리커니 2018. 3. 20.
반응형

 

javascript 동적 멀티파일 업로드, 동적으로 선택된 파일 전달, ajaxForm 활용 fileList 컨트롤,  dynamic file upload

 

우선 멀티 업로드에 대한 전반적인 설명은 아래의 Link로 대체 하겠습니다.

 

Link1 : javascript 업로드할 이미지 미리보기 image preview URL.createObjectURL

 

Link2 : javascript spring 멀티파일선택 업로드 ajaxForm multipart/form-data MultipartHttpServletRequest

 

Link3 : Java Spring File Upload MultipartHttpServletRequest 멀티파일 업로드 방법

 

 

 

 

첫 번째 Link를 보시면, 마지막에 질문을 던졌죠.

멀티로 파일을 선택하게 되면 file 태그 객체에 fileList가 생성이 되게 됩니다.

그러면서 선택된 파일이 배열로 들어가게 되죠.

하지만 fileList는 보안상의 이유로 수정이나 삭제를 할 수 없습니다. reaonly

그렇기 때문에 이를 대체 할 수 있는 객체를 만들어서 컨트롤 하여야 하는데요,

 

결론부터 말씀드리면,

fileList를 대체 할 수 있는 배열 객체를 만들어 fileList를 복사하여 넣고

새로 만든 배열 객체로 컨트롤 하여 서블릿으로 전달 하면 되는 것입니다.

 

그럼 코드를 보도록 하죠.

file태그는 form태그 밖으로 빼줍니다. (이 객체를 전달 할 것이 아니기 때문에)

 

1
<input multiple="multiple" name="files[]" id="files" type="file" style="display:none"/>
cs

 

그리고 파일이 변경될때 전역배열객체 fileBuffer에 file 을 넣어줍니다. (line 5)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    $('#files').change(function(){
        fileBuffer = [];
        const target = document.getElementsByName('files[]');
        
        Array.prototype.push.apply(fileBuffer, target[0].files);
        var html = '';
        $.each(target[0].files, function(index, file){
            const fileName = file.name;
            html += '<div class="file">';
            html += '<img src="'+URL.createObjectURL(file)+'">'
            html += '<span>'+fileName+'</span>';
            html += '<span>기간 '+'<input type="text" style="width:250px/"></span>';
            html += '<a href="#" id="removeImg">╳</a>';
            html += '</div>';
            const fileEx = fileName.slice(fileName.indexOf("."+ 1).toLowerCase();
            if(fileEx != "jpg" && fileEx != "png" &&  fileEx != "gif" &&  fileEx != "bmp" && fileEx != "wmv" && fileEx != "mp4" && fileEx != "avi"){
                alert("파일은 (jpg, png, gif, bmp, wmv, mp4, avi) 형식만 등록 가능합니다.");
                resetFile();
                return false;
            }
            $('.fileList').html(html);
        });
 
    });
cs

 

이제 동적으로 생성된 미리보기 행의 X 버튼을 클릭했을 때 이벤트를 등록합니다.

 

1
2
3
4
5
$(document).on('click''#removeImg'function(){
    const fileIndex = $(this).parent().index();
     fileBuffer.splice(fileIndex,1);
     $('.fileList>div:eq('+fileIndex+')').remove();
});
cs

 

splice를 활용해 해당 index가 같은 객체 1개를 삭제합니다.

 

 

 

 

이제 3개의 파일을 선택한 후 하나의 파일을 제거 했을 때 fileBuffer와 file태그의 fileList를 확인해 보도록 하겠습니다.

위의 X버튼 클릭 콜백함수 마지막에 콘솔을 찍어보도록 하죠.

 

1
2
3
4
5
6
7
8
9
10
$(document).on('click''#removeImg'function(){
    const fileIndex = $(this).parent().index();
     fileBuffer.splice(fileIndex,1);
     fileArray.splice(fileIndex,1);
     $('.fileList>div:eq('+fileIndex+')').remove();
     
    const target = document.getElementsByName('files[]');
    console.log(fileBuffer);
    console.log(target[0].files);
});
cs

 

결과는 아래와 같습니다.

 

 

위에서 설명한 것과 같이 file태그의 fileList는 readonly 기 때문에 변경할 수 없습니다. 그래서 이를 대체 하기 위해 fileBuffer라는 배열객체를 만들었죠.

 

이제 이 fileBuffer를 컨트롤러로 전달 하도록 하겠습니다.

위의 2번째 Link를 보시면 ajaxForm을 활용한 파일객체 전달에 대해 설명이 되어 있습니다.

하지만 위의 Link는 선택된 파일 객체를 그대로 전달하는 방법이고,

이제는 수정된 fileBuffer를 넘겨야 하기 때문에 약간의 수정이 필요합니다.

 

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
ajaxForm : function (id, file, func){
                $('#'+id).ajaxForm({
                    contentType : false,
                    processData: false,
                    enctype: "multipart/form-data",
                    dataType : "POST",
                    dataType : 'json',
                    beforeSubmit: function(data, form, option) {
                        var fileSize = file.length;
                        if (fileSize>0){
                            for(var i=0; i<fileSize; i++){
                                var obj = {
                                        name : "files[]",
                                        value : file[k],
                                        type : "file"
                                };
                                console.log(obj);
                                data.push = obj;
                            }    
                        }
                        console.log('beforeSubmit');
                        console.log(file);
                        console.log(data);
                        console.log(form);
                        console.log(option);
                    },
                    success: function(returnData) {
                        console.log("returnData : "+returnData);
                        func(returnData);
                    },
                    error: function(x,e){
                          console.log("[AF]ajax status : "+x.status);
                          console.log(e);
                      },
                });
            },
cs

 

매개변수로 file이 추가 되었습니다. 이 것이 fileBuffer 입니다.

그리고 서브밋을 하기 전에 이 fileBuffer를 가지고 오브젝트를 만들어 데이터에 넣어주기만 하면 되죠.

beforeSubmit 함수를 보시면, 기존의 데이터 index 이후에 file에 대한 object를 만들어 추가해 주었습니다.

이렇게 하면 일반 file 태그의 fileList를 넘길 때와 같은 구조가 되게 됩니다.

 

일반적으로 file 태그의 fileList를 넘길 때와 fileBuffer를 넘길 때를 각각 콘솔로 찍어보면

같은 구조인 것을 확인 하실 수 있습니다.

 

이제 컨트롤러에서는 MultipartHttpServletRequest 를 사용하여 file을 받을 수 있습니다.

Link2, Link3 참고.

 

※ IE, 크롬의 경우 차이가 있습니다.

IE의 경우 data에 file type이 전달되지 않습니다. Chrome의 경우에는 전달됨.

그렇기 때문에 IE의 경우 추가로 데이터를 push 해주시면 되지만,

 chrome 의 경우 삭제,추가 하거나 변경해주셔야 합니다.

(data객체 확인)

 

 

추가적으로 파일 객체를 초기화 하는 방법을 알아보겠습니다.

위에서 말씀드린데로 파일 객체는 보안상 수정이 되질 않습니다.

그렇기 때문에 기능상 초기화를 해야 할 때가 있는데요. 그 방법을 알아보겠습니다.

 

1
2
3
4
5
6
var agent = navigator.userAgent.toLowerCase();
if ((navigator.appName == 'Netscape' && navigator.userAgent.search('Trident'!= -1|| (agent.indexOf("msie"!= -1) ){
    $(target).replaceWith( $(target).clone(true));
else {
    $(target).val("");
}
cs

 

navigator를 사용하는 이유는 브라우져를 구분하기 위해서 입니다.

위 코드에서 보시는바와 같이 IE, Netscape, Trident 만 구분을 해 줍니다.

다른 브라우져의 경우 value만 초기화 해주시면 되지만 위의 세 브라우져는 방식이 다릅니다.

참고로 위의 코드는 파일객체의 change 함수내에 있습니다.

 

replaceWith 함수의 경우 요소를 대체하는 함수인데요.

위에서는 target 객체를 함수 파라미터 객체로 대체하는 함수입니다.

 

clone 함수는 객체를 복사하는 함수입니다. 파라메터에 true를 주면 그 요소가 가지고 있는 이벤트까지 복사가 됩니다. false는 반대.

 

결론적으로 해당 요소를 복사해(이벤트포함) 요소로 대체를 하는 것이죠.

반응형

댓글

💲 추천 글