[Input_type="file"]_업로드 구현을 위한 프론트 부분

2022. 4. 29. 10:03[프론트엔드]_/[Javascript]_ES6

728x90
반응형

포스팅 목표]

 

파일 업로드 방식 중 많이 사용하는 <input type="file"> 을 사용해

여러파일을 등록 가능하게 하며,

등록된 파일의 썸네일 표출 , line 별 삭제기능 구현 방식 설명

 

완료화면]

한가지 or 다중 파일 선택시 하단에 썸네일, 이름 , 삭제버튼 생성

HTML ]

<div id="contentwrap2">

    <form id="formdata">
        <h3> 제목 : <input type="text" placeholder="제목" id="subject" name="subject"></h3>
        <h3> 내용 : <input type="text" placeholder="내용" id="writecontent" name="content"></h3>
        <h3 class="fileuploder"> 파일첨부 :
            <label for="ex_file">파일 등록</label>
            <input type="file" id="ex_file" name="filename" multiple>
        </h3>
    </form>

    <ul class="lst_thumb">

    </ul>

    <button type="button" class="reg">등록</button>
    <button type="button" class="backbtn">취소</button>

</div>

</body>
</html>

Input type="file" 을 그대로 사용하면, 버튼부분과 text 부분으로 나뉘는데,

동적변환시 해당 text가 바로바로 바뀌지 않아서 label 을 사용하여 클릭이벤트를 위임하고,

css를 사용해서 input 을 숨길 것입니다.

 

또한 여러 파일을 전달할 수 있도록 multiple 속성을 부여합니다.

 

CSS]

.fileuploder label {
    display: inline-block;
    padding: .5em .75em;
    font-size: inherit;
    line-height: normal;
    vertical-align: middle;
    background-color: #cae1f2;
    cursor: pointer;
    border: 1px solid #ebebeb;
    border-bottom-color: #e2e2e2;
    border-radius: .25em;
}

.fileuploder input[type="file"] {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    border: 0;
}

Script]

 

1. label 클릭시 이벤트 부분]

const imageTag = document.getElementById("ex_file");
imageTag.addEventListener('change', function () {
    console.log('파일선택');
    while (onnode.hasChildNodes()) {
        onnode.removeChild(onnode.firstChild);
    }
    loadImg(this);

});

type= file 의 경우 파일을 선택해서 확인버튼을 누르는 순간

onchange 이벤트가 걸립니다.

 

따라서 해당 부분에 EventListener 를 change로 걸고

 

파일이 등록되어 있을경우 다시 그 버튼을 클릭하면 reset이 됩니다

따라서 목록부분도 지워야 하기 때문에 removechild 순회로 자식노드를 삭제합니다.

 

그리고 해당 정보를 섬네일 생성 함수로 전달합니다.

const imageTag = document.getElementById("ex_file");

정보는 해당 imageTag 에 걸린 데이터들을 말합니다.

 

2. 섬네일 생성, 리스트 생성 함수]

function loadImg(value) {

    for (let i = 0; i < value.files.length; i++) {
        console.log("in")
        if (value.files && value.files[i]) {

            let reader = new FileReader();

            let fullname = document.getElementById("ex_file").files[i].name;
            let str = fullname.split('.');
            let ext = str[1];
            console.log("확장자: " + ext);

            let node = document.createElement('li');
            let tmp = `
            <li><img scr="" class="uploadimage">
                    \${fullname}
                <input type="button" class="rmbtn" value="삭제">
            </li>
        `
            node.innerHTML = tmp;

            node.querySelector('.rmbtn').onclick = function () {
                node.remove();
                const dataTransfer = new DataTransfer();
                let trans = $('#ex_file')[0].files;
                let filearray = Array.from(trans);
                filearray.splice(i, 1);
                filearray.forEach(file => {
                    dataTransfer.items.add(file);
                });
                $('#ex_file')[0].files = dataTransfer.files

            }


            if (ext == "txt") {
                onnode.appendChild(node)
                node.querySelector("img").setAttribute('src', "/assets/img/textfile.jpg");
            } else {
                reader.onload = function (e) {
                    onnode.appendChild(node)
                    node.querySelector("img").setAttribute('src', e.target.result);
                }
            }

            reader.readAsDataURL(value.files[i]);
        }

    }

}

 

2-1] FileList 객체

 

여기서 중요한 점은 input type="file" 에서 선택된 파일들의 정보는 어디로 담기게 될까요

 

FileList 라는 객체입니다. 해당 객체는

요소의 HTMLInputElement.files 속성으로 불러올 수 있으며

선택한 파일의 목록을 FileList 라는 객체로 반환합니다.

FileList 는 배열처럼 사용가능합니다. 

이 말을 Index 번호로 참조 가능하다는 의미입니다.

 

value 는 곧 HTMLInputElement 가 됩니다.

따라서 선택된 파일의 수 만큼 순회를 돌게 됩니다.

 

2-2] FileReader 생성자

let reader = new FileReader();

input type="file" 에서 반환되는 FileList에 부착할 수 있는 FileReader 입니다.

 

File 객체는 <input> 태그를 이용하여 유저가 선택한 파일들의 결과로 반환된 FileList 객체,
드래그 앤 드랍으로 반환된 DataTransfer 객체 혹은 HTMLCanvasElement의 mozGetAsFile() API로 부터 얻습니다.

 

2-3] 파일 이름, 확장자 분리

let fullname = value.files[i].name;

업로드 된 파일의 전체이름을 받아옵니다.

let str = fullname.split('.');
let ext = str[1];
console.log("확장자: " + ext);

이름 중 확장자는 . 을기준으로 나뉘기 때문에 split으로 나누고

. 기준으로 나뉜 2부위를 각각 밸로 반환합니다.

확장자는 Index[1]에 입력되어 있습니다.

 

2-4] 선택한 파일 리스트 만들기

let node = document.createElement('li');
    let tmp = `
    <li><img scr="" class="uploadimage">
            \${fullname}
        <input type="button" class="rmbtn" value="삭제">
    </li>
`
    node.innerHTML = tmp;

    node.querySelector('.rmbtn').onclick = function () {
        node.remove();
        const dataTransfer = new DataTransfer();
        let trans = $('#ex_file')[0].files;
        let filearray = Array.from(trans);
        filearray.splice(i, 1);
        filearray.forEach(file => {
            dataTransfer.items.add(file);
        });
        $('#ex_file')[0].files = dataTransfer.files

    }

<ul> 아래에 리스트로 들어갈 li 엘리먼트를 만들고

그안에 html을 tmp 의 양식을 따릅니다.

 

이미지가 들어갈 공간과, 삭제버튼을 만들고

 

삭제버튼 이벤트를 등록합니다.

우선 해당 노드를 지웁니다.

 

보여지는 부분 삭제는 간단합니다만, input type="file" 을 삭제하는 부분은 조금 까다롭습니다.

 

우선 FileList 객체에서 받아온 값은 인덱스로 접근 가능하나,

통상적으로 배열에서 사용하는 splice 같은 함수가 작동을 하지 않습니다.

 

따라서 먼저 Array.form 으로 변환을 시키고

splice 함수를 사용해서 해당 인덱스 번호의 값을 삭제합니다.

dataTransfer.items.add(file) 을 통해 지정된 데이터를 사용하여 새 항목을 만들고

dataTransferItem 목록에 추가합니다.

 

HTMLInputElement 요소의 0번째 인덱스 에 저장된 Transferitem 의 files 형식을 붙여서 치환시킵니다.

 

2-5] 확장자에 따른 처리

if (ext == "txt") {
    onnode.appendChild(node)
    node.querySelector("img").setAttribute('src', "/assets/img/textfile.jpg");
} else {
    reader.onload = function (e) {
        onnode.appendChild(node)
        node.querySelector("img").setAttribute('src', e.target.result);
    }
}

reader.readAsDataURL(value.files[i]);

우선 확장자가 txt일 경우 notepade 이미지를 별도로 지정해서 보여주도록 하였고,

나머지는 파일의 섬네일을 보여주게 하였습니다.

이미지가 아닐 경우에는 깨진파일 모양이 나오기 때문에 별도로 사진을 등록한 것입니다.

만약 다른 형식의 파일 확장자는 else if 를 추가하시면 됩니다.

 

섬네일 부분만 설명드리면

 

reader.onload -> load 이벤트의 핸들러. 이 이벤트는 읽기 동작이 성공적으로 완료 되었을 때마다 발생합니다.

reader가 성공적으로 파일이 업로드 된것을 확인하면 앞에 작성한 node 를 만들고 해당 노드의 img 라는 부분에

src 값을 올라간 타겟의 결과값으로 보여주도록 합니다.

 

이렇게 하게되면 프론트적 구성은 끝났습니다.

남은 부분은 등록버튼 클릭시 해당 데이터들을 formData 객체에 묶어서 통신하면 됩니다.

 

감사합니다.

728x90
반응형