2022. 5. 15. 18:06ㆍ[프론트엔드]_/[Javascript]_ES6
목적 ] Jquery 와 ES6 를 사용하여 input type="file" 의 다중 업로드 및 제약조건 부여하기
Front 부분]
<span class="inputWrap fileBoxWrap">
<input type="text" class="inp lineType" id="file_name"
style="width:calc(100% - 85px);" placeholder="이미지 파일만 첨부 가능합니다." readonly/>
<label for="filename" class="btn btn-whiteG"><span class="text">찾아보기</span></label>
<input type="file" class="file hidden" id="filename" name="attach_file"
accept=".avif,.gif,.jpeg,.png,.svg+xml,.webp,.bmp">
</span>
label 에 for를 이용하여 디자인을 입힐 수 있습니다.
input type="file" 에 accept 속성을 이용하여 파일 첨부시 보여지는 파일을 정할 수 있습니다.
단 완전하게 막지는 못하기 때문에 Controller 부분에서 제어를 해줘야 합니다.
Script 부분]
1] 사용할 DataTransfer 객체와 총 파일 업로드 가능한 수 선언
const dt = new DataTransfer();
let totalCount = 5;
2] 파일 업로드 될 때 이벤트 걸기
$('#filename').on('change', function () {
부분으로 파일이 업로드 ( = change) 될 때 이벤트를 겁니다.
3] 접근을 쉽게하기 위한 변수 선언
let files = this.files;
해당 선언을 통해 선택한 element 에서의 files 라는 목록을 가져옵니다.
input type="file" 의 경우 선택된 파일을 fileList 로 반환합니다.
해당 엘리먼트는 files 안에 리스트 구조로 값이 들어오게 됩니다. 이를 사용하기 쉽게 선언합니다.
4] 파일 선택시 선택된 파일 이름을 출력할 text 구간 만들기
$(this).closest('.fileBoxWrap').find('input.lineType').val($.map(files, function (item) {
return item.name;
}).join(', '));
JQuery -> closest 함수
var closestElement = targetElement.closest(selectors);
해당 엘리먼트 에서부터 selectors에 지정한 선택자에 만족할 때 까지 탐색합니다. 이중 가장 조건에 만족한 부모 요소가 반환되며, 없을시 null을 반환합니다.
JQuery -> find 함수
.find( selector )
closest 로 반환된 부모 요소로부터 input.lineType 이라는 클래스를 가진 곳을 찾습니다.
해당 lineType 의 경우는 파일 업로드 시 해당 파일의 이름을 사용자에게 보여주기 위한 text 값 입니다.
JQuery -> val() 를 사용하여 해당 엘리먼트에 값을 집어넣습니다.
그 값은 JQuery -> map 을 사용하여, fileList 로 받아온 값 을 새로운 배열로 만들게 됩니다.
그 값은 해당 name 의 요소를 가져오며
JQuery -> Join을 이용하여 이를 배열로 만듭니다.
5] 현제 선택된 파일들의 배열
let filesArr = Array.from(dt.files);
Array.from 을 사용하여 dt.files 에 저장된 값들을 배열로 만듭니다.
6] 허용 가능한 이미지 값들의 배열
let imagearr = ['image/apng','image/avif','image/gif','image/jpeg','image/png','image/svg+xml','image/webp','image/bmp']; //등록 가능한 이미지 목록
7] 파일 크기 검사
if (files[0].size > 20000000) {
common.alert("첨부 파일 개별 사이즈는 20MB를 넘을 수 없습니다.")
$('#filename')[0].files = dt.files;
$('#file_name').val(""); // 가이드 텍스트 비우기
}
선택한 파일 의 사이즈를 구분하기 위해서는 index 0번째의 size 요소의 값을 불러옵니다
해당 size는 byte 사이즈 이며 20MB 는 20000000 입니다.
여기에서 아래의 부분이 가장 중요합니다.
$('#filename')[0].files = dt.files;
해당 type="file" 에 files 부분에 dt.files 를 함으로써,
선택된 파일을 무시하고, 이전에 담겼던 정보를 붙이는 방식입니다.
무조건 index 는 0 이여야 하며,
예를들어 파일을 3개를 업로드 한 상태에서 이번 파일이 실패할 경우 이전의 정보를 붙임으로써,
사이즈를 넘긴 파일이 담긴 list 대신에 선택되기 전 상태의 list를 붙이는 방식입니다.
8] 이미지 양식 비교
else if(!(imagearr.includes(files[0].type))){
common.alert("이미지 파일만 등록 가능합니다.")
$('#filename')[0].files = dt.files;
$('#file_name').val(""); // 가이드 텍스트 비우기
}
이전에 선언한 이미지 배열에 입력된 files[0] 의 타입을 비교합니다.
includes(target) 함수를 이용해서 입력된 값이 배열에 있을시 true를, 없을 시 false를 리턴합니다.
7번과 같은 로직을 사용하여 이미지 파일이 허용되지 않을 경우 선택되지 않도록 합니다.
9] 최대 등록 수 5개를 넘지 않도록 제어
else {
if (filesArr.length >= totalCount) {
common.alert("첨부파일은 " + totalCount + "개 까지만 등록 가능합니다.")
$('#filename')[0].files = dt.files;
$('#file_name').val(""); // 가이드 텍스트 비우기
} else {
$.each(files, function (i, item) {
$('#fileList').append('<div class="w100p">' + item.name + '<button type="button" class="btn btn-white textBtn" style="margin-left: 5px;" onclick="removeFile(this, ' + (dt.items.length + i) + ');"><i class="ambicon-015_mark_times"></i></button></div>');
});
for (let file of files) {
dt.items.add(file);
}
this.files = dt.files;
}
}
크기, 타입 검사 이후 총 수량 검사부분입니다.
위에서 선언한 filesArr 의 경우 새로운 파일 이벤트 걸기전 files[0]의 상태를 나타낸다고 생각하면 됩니다.
따라서 이전까지 업로드한 수가 5개 이고 이번 이벤트로 추가시 6이 될것이기 때문에 >=를 사용하였습니다.
지우는 로직은 동일합니다.
위의 로직을 전부 통과 할 경우
$.each(files, function (i, item) {
$('#fileList').append('<div class="w100p">' + item.name + '<button type="button" class="btn btn-white textBtn" style="margin-left: 5px;" onclick="removeFile(this, ' + (dt.items.length + i) + ');"><i class="ambicon-015_mark_times"></i></button></div>');
});
files의 총 요소들 만큼 반복하면서
append 함수를 통해 해당 엘리먼트를 생성합니다.
닫기 버튼 클릭시 removeFile라는 함수에 해당 엘리먼트와 해당 item의 인덱스 번호를 전달합니다.
for (let file of files) {
dt.items.add(file);
}
this.files = dt.files;
해당 함수를 통해 dt 객체에 똑같이 저장시킵니다.
그리고 input 요소의 files 에 저장한 dt속의 files를 지정함으로써 실질적으로 input 요소에 저장되게 됩니다.
10] 삭제버튼 클릭 함수
function removeFile(element, index) {
$(element).closest('div').remove();
for (let i = 0; i < dt.items.length; i++) {
if (index === i) {
dt.items.remove(i);
continue;
}
}
$('#filename')[0].files = dt.files;
}
삭제시 해당 요소와 그 index 번호를 전달 받습니다.
우선 엘리먼트를 삭제합니다. 부모요소로부터 div는 한가지이니 해당 엘리먼트가 삭제되고,
for 문을 통해서 등록된 수 만큼 반복하게 됩니다.
만약 해당 전달받은 인덱스와 dt에 저장된 인덱스 번호가 같을 때 해당 인덱스의 아이템을 삭제합니다.
그리고 나서 실질적인 input 요소에 해당 dt.files를 전달함으로써 삭제가 이루어지게 됩니다.
이렇게 DataTransfer() 를 사용한 파일 업로드를 알아보았습니다.
dt.files가 이전에 저장한 정보들이라고 생각하게 되면 로직을 짜기 쉽습니다.
감사합니다.
'[프론트엔드]_ > [Javascript]_ES6' 카테고리의 다른 글
[Window.history]_history.back() 감지 함수 (0) | 2022.05.26 |
---|---|
[이벤트 위임]_ul li 이벤트 위임. (0) | 2022.05.24 |
[Input_type="file"]_업로드 구현을 위한 프론트 부분 (2) | 2022.04.29 |
[코드 컨펌]_Template 부분_(Feat. TAB구현 심화) (0) | 2022.03.04 |
[TAB 구현 심화_3]_Spring에서 tab 구현해보기 (feat 메모리 저장) (0) | 2022.02.22 |