Study

파일업로드 input[files] FileList 동적으로 변경하기

voider 2021. 6. 12. 19:28

파일업로드 시 input태그 FileList 동적으로 변경하기

파일업로드 기능을 구현하면서 겪은 문제.
사용자가 파일을 업로드하면 미리보기 형식으로 자신이 어떤 파일을 올렸는지 확인할 수 있어야 한다. 혹여 잘못 올린 파일이 있다면 해당 파일만 따로 삭제할 수 있어야 한다.

예시 :

이때 X버튼을 눌러서 화면에서 파일을 지워줘도 File Input Tag에는 그대로 해당 파일이 남아있다.

예시: 업로드한 파일 8개 중 7개를 지웠는데 화면에서는 삭제됐지만, Input창에는 그대로 남아있다.

File Input 내부적으로 files라는 FileList를 가지고 있다.

문제는 FileList의 값을 바꿀 수 없기 때문에 삭제된 파일을 input tag에 반영할 수 없다는 것이다. files의 값을 변경하려고 시도하면 다음과 같은 에러가 나온다.

//삭제되지 않은 파일만 files에 덮어쓰기
document.querySelector('#file-input').files =
            Array.from(files).filter(file => file.lastModified !== removeTarget);

----------------------------console-------------------------------
Uncaught TypeError: Failed to set the 'files' property on 'HTMLInputElement': Failed to convert value to 'FileList'.
    at HTMLDocument.<anonymous>

Javascript가 제공하는 DataTransfer 객체로 이 문제를 해결할 수 있다. MDN DataTransfer문서를 보면 Drag and drop API라고 소개한다. MDN FileList를 보면 Drag and drop할 때 FileList를 사용한다고 되어 있다.

DataTransfer를 이용하여 FileList의 값을 변경할 수 있었다.

const dataTranster = new DataTransfer();

Array.from(files)
    .filter(file => file.lastModified != removeTargetId)
    .forEach(file => {
        dataTranster.items.add(file);
    });

document.querySelector('#file-input').files = dataTranster.files;

DataTransfer가 가진 itemList에 삭제되지 않은 파일을 필터링해서 추가하고 반환할 때는 items가 아닌 files를 반환해야 한다.

전체 코드

html.body

    <input type="file" id="file-input" multiple />
    <h3>업로드된 파일</h3>
    <div id="preview">
    </div>

javascript

        const handler = {
            init() {
                const fileInput = document.querySelector('#file-input');
                const preview = document.querySelector('#preview');
                fileInput.addEventListener('change', () => {  
                    console.dir(fileInput)                  
                    const files = Array.from(fileInput.files)
                    files.forEach(file => {
                        preview.innerHTML += `
                        <p id="${file.lastModified}">
                            ${file.name}
                            <button data-index='${file.lastModified}' class='file-remove'>X</button>
                        </p>`;
                    });
                });
            },

            removeFile: () => {
                document.addEventListener('click', (e) => {
                if(e.target.className !== 'file-remove') return;
                const removeTargetId = e.target.dataset.index;
                const removeTarget = document.getElementById(removeTargetId);
                const files = document.querySelector('#file-input').files;
                const dataTranster = new DataTransfer();

                // document.querySelector('#file-input').files =
                //             Array.from(files).filter(file => file.lastModified !== removeTarget);

            
                Array.from(files)
                    .filter(file => file.lastModified != removeTargetId)
                    .forEach(file => {
                    dataTranster.items.add(file);
                 });
    
                document.querySelector('#file-input').files = dataTranster.files;

                removeTarget.remove();
            })
            }
        }

        handler.init()
        handler.removeFile()

'Study' 카테고리의 다른 글

테스트 대역  (0) 2021.12.21
@ParameterlizedTest  (0) 2021.09.08
[Database] Transaction, Lock  (0) 2021.05.25
Flyway  (0) 2021.05.16
인프런 Kotlin으로 개발하는 Spring Boot Web MVC - Web개론  (0) 2021.05.15