글에서 파일 드래그 앤 드랍한 파일들을 AJAX로 전송하는 법을 알아보았당.
구지~ 구지~ 하고 싶진 않지만 form태그를 이용하여 구현/전송하려는 호기심 만땅
소수 열정파들이 있어 form 태그로 전송하는 것도 억지로 구현해보장.
먼저 해결해야 할 문제를 인식해 보장
form 태그를 이용하여 전송하려면 input type=file을 이용해야 하는뎅, 이전 글처럼
사용자가 파일묶음을 마우스로 끌고 왔을 때, 개별 파일의 전송 여부를 체크박스로
제어하려면, 끌고 온 파일들을 개별로 input type=file에 넣어주어야 하는뎅,
요기서 어려움이 발생한당.
input type=file의 files 속성(FileList 인터페이스)은 읽기 전용이란 사실이당
아마도 보안때문일 것인데, 먼저 이 부분이 해결가능한가? 를 알아봐야 한당.
우앙 천만다행이당.
files속성이 읽기 전용이긴 한데, 같은 타입으로 통째로 덮어쓰는 건 가능하단당.
곧 마우스로 끌고 온 파일들을 아래처럼 input type=file에 넣을 수 있단 이야기당.
합리적이당! 뭔가 될 것 같당.
//input type=file 태그의 id가 myFiles라고 했을 때
myFiles.files = event.dataTransfer.files
하지만 우리가 원하는 건 개별 컨트롤을 위해, 끌고 온 파일들을 꺼내, 파일 1개당
1개의 input type=file에 맵핑 시켜야 자유롭게 선택이 가능하당.
여기서 그리 자주 쓰이지 않는 DataTransfer 의 직접 사용이 필요하당.
아래 코드를 한번 누느로 위에서 아래로 휘리릭 하장.
의미만 파악해서, 거의 그대로 사용하면 크게 문제 없을 거시당!.
// 끌고 온 파일들
let v_files = event.dataTransfer.files;
for (let i = 0; i < v_files.length; i++) {
let dataTransfer = new DataTransfer();
//1개의 dataTransfer에 1개의 file을 담는당
dataTransfer.items.add(v_files[i]);
//디버깅용 로그
console.log("dataTransfer", dataTransfer.files);
// input type=file 생성
let inFile = document.createElement("input");
inFile.type = "file";
inFile.name = "myFiles";
//개별 파일 할당!
inFile.files = dataTransfer.files;
//inFile을 원하는 DOM에 appendChild하면 눈으로 볼 수 있당.
}
파일의 전송여부는 input type=file 의 disabled 속성으로 제어할 것이당.
disabled된 것은 form 전송시 서버로 전송되지 않음을 알고 있디용?
아래처럼 체크박스의 체크여부로 disabled 속성을 부여하므로써,
이전 글의 f_ckFileList 함수는 더이상 필요없게 된당.
chkBox.addEventListener("change", function () {
//바뀐 부분
let selectImg = document.querySelector("#img" + this.fileId);
let selectFile = document.querySelector("#file" + this.fileId);
if (this.checked) {
selectImg.style.display = "inline-block";
selectFile.disabled = false;
} else {
selectImg.style.display = "none";
selectFile.disabled = true;
}
})
떤송 버튼 클릭시 실행되는 f_send함수는 사실상 이젠 그냥 전송만 하면 되지만
백엔드를 구현하지 않았으니, input type=file 객체만을 로그로 찍어서,
전송될 것과 전송 되지 않을 것을 누느로 구분할 수 있는 코드로 아래처럼 맹글었당.
// 체크박스에 체크된 파일만 전송
function f_send() {
//선택된 것만 전송대상인지 확인을 위한 로그
for (let elem of myForm.elements) {
if (elem.type == "file") {
if (elem.disabled) {
console.log("전송 안될것:", elem);
} else {
console.log("전송 될것:", elem);
}
}
}
//myForm.submit(); // 백엔드를 준비했다면 열어랑!
}
뽀인또 설명은 끝났으니, 이제 전체 소스를 붙인당.
복사/붙여넣기와 실행/코드해석 따라가기로 전체흐름을 이해 할 수 있을거시당.
formSubmitDragAndDrop.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>E7E Drag Drop</title>
<style>
#wrapper {
width: 350px;
}
#preview {
width: 100%;
height: 300px;
border: 2px solid pink;
background-image: url("https://blog.kakaocdn.net/dn/bvQO1s/btraInYH9xW/oekuwou6IKTnzIHKD40ykK/img.png");
background-size: 110% 110%;
background-position: -20px -20px;
overflow: auto;
}
#list {
width: 100%;
border: 2px solid black;
height: 200px;
overflow: auto;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
</head>
<body>
<h1>외부파일 끌다노킹 이해</h1>
<div id="wrapper">
<div id="preview" ondragover="f_over()" ondrop="f_drop()">
</div>
<form id="form1" action="/서버UL" method="post" enctype="multipart/form-data">
<div id="list">
<h3>파일 리스트</h3>
<hr>
</div>
<button type="button" onclick="f_send()">떤송</button>
</form>
</div>
<script>
// 고정 전역변수(동적생성 아닌 DOM)
const myPreview = document.querySelector("#preview");
const myList = document.querySelector("#list");
const myForm = document.querySelector("#form1");
// DROP 이벤트 처리
let attachFileList = []; // 첨부파일 리스트를 위한 전역변수
let fileIdIndex = 1; // 첨부파일(이미지) 아이디 시작넘버
// 체크박스에 체크된 파일만 전송
function f_send() {
//선택된 것만 전송대상인지 확인을 위한 로그
for (let elem of myForm.elements) {
if (elem.type == "file") {
if (elem.disabled) {
console.log("전송 안될것:", elem);
} else {
console.log("전송 될것:", elem);
}
}
}
//myForm.submit(); // 백엔드를 준비했다면 열어랑!
}
//파일 중복 여부 체크 함수, 일단 그냥 파일명으로 비교
function f_isRepeat(pFile) {
for (let i = 0; i < attachFileList.length; i++) {
if (pFile.name == attachFileList[i].name) {
return true; // 중복
}
}
return false; // 안 중복
}
// 파일 1개씩 비동기로 읽어서 처리
function f_readOneFile(pInx, pFile) {
// 파일중복여부 체크
if (f_isRepeat(pFile)) {
Swal.fire({
title: "미안행~~",
text: "너가 이미 선택한 파일이얌!!~~ㅠㅠ",
icon: "warning"
});
return; // 그냥 종료
}
attachFileList.push(pFile);
// console.log("파일리스트:", attachFileList);
// 파일 읽어주는 아저씨 생성, 사람은 바이너리 파일을 못 읽음
let v_fileReader = new FileReader();
// 끌어온 파일 읽으라고 지시
v_fileReader.readAsDataURL(pFile);
// 파일 내용을 다 읽었다면, load이벤트 발생(비동기)
// 읽은 결과는 result속성에 담기게 된당.
v_fileReader.onload = function () {
// 비동기로 처리됨을 확인할 로그
// console.log("체에킁:", pInx, pFile);
let v_img = document.createElement("img");
v_img.setAttribute("src", v_fileReader.result);
v_img.id = "img" + fileIdIndex;
v_img.style.width = "50%";
v_img.style.height = "50%";
// checkbox와 text를 가진 div 맹글어서 추강!
let fileDetail = document.createElement("div");
let chkBox = document.createElement("input");
chkBox.type = "checkbox";
chkBox.value = pFile.name;
chkBox.fileId = fileIdIndex;
chkBox.checked = true; // 디폴트 체크
chkBox.addEventListener("change", function () {
//바뀐 부분
let selectImg = document.querySelector("#img" + this.fileId);
let selectFile = document.querySelector("#file" + this.fileId);
//console.log("체에킁:", selectFile);
if (this.checked) {
selectImg.style.display = "inline-block";
selectFile.disabled = false;
} else {
selectImg.style.display = "none";
selectFile.disabled = true;
}
})
// 중요하다기 보단, form태그 전송을 위한 어쩔 수 없는 선택!
let dataTransfer = new DataTransfer();
dataTransfer.items.add(pFile);
let inFile = document.createElement("input");
inFile.type = "file";
inFile.name = "myFiles";
inFile.id = "file" + fileIdIndex;
inFile.style.display = "none"; // 화면에서 감추깅
//읽기전용이지만 fileList 통째로 덮어쓰기 가능
inFile.files = dataTransfer.files;
//id값 증가
fileIdIndex++;
let txtBox = document.createElement("input");
txtBox.type = "text";
txtBox.readOnly = true;
txtBox.style.border = "none";
txtBox.value = pFile.name;
fileDetail.appendChild(chkBox);
fileDetail.appendChild(txtBox);
fileDetail.appendChild(document.createElement("br"));
myPreview.appendChild(v_img);
myList.appendChild(fileDetail);
console.log("체킁:", inFile.files);
myList.appendChild(inFile);
//스크롤바 아래로 내리깅!
myPreview.scrollTo(0, myPreview.scrollHeight);
myList.scrollTo(0, myList.scrollHeight);
}
}
//브라우져가 지원하는 파일 자동으로 여는 거 막기 위함
function f_over() {
event.preventDefault();
event.stopPropagation();
}
// Drop 이벤트 기본기능 막고, 원하는 기능 넣기
function f_drop() {
event.preventDefault();
event.stopPropagation();
// 마우스로 끌어온 파일, 일단 1개만
let v_files = event.dataTransfer.files;
for (let i = 0; i < v_files.length; i++) {
f_readOneFile(i, v_files[i]);
}
}
// Drop 영역외에 파일 끌어다 놓았을 때 브라우져 동작막깅
window.addEventListener("dragover", function () {
event.preventDefault();
});
window.addEventListener("drop", function () {
event.preventDefault();
});
</script>
</body>
</html>
실행 결과 아래처럼 잘 되었당. 개인적으론 AJAX사용을 많이 선호해서
필요없지만, 필요한 사람에게 도움이 되었으면 하는 바램이당.
이거스로 파일 드래그 앤 드랍 응용은 당신에게 넘겨졌당!(당신 손은 마술사!)
결정된 옳고 그름도
결정된 선과 악도
결정된 정답과 오답도
없는게 아니라, 있을 수 없음을 알게 된 나이
내 속의 나, 또 다른 나 그래서 우린
더이상 싸울 이유를 찾지 못했당.
오늘도 타협의 조그셔틀을 돌린당.
우린 더 이상 싸우지 않는당!
적어도 보이는 싸움은.....
https://www.youtube.com/watch?v=nnIHW-aDhZk
fetch API를 Promise를 써 직접 맹글깡? (2) | 2024.04.09 |
---|---|
Promise(약속) 모르면 바보옹(비동기) (0) | 2024.01.08 |
외부파일 드래그 앤 드롭2(File Drag and Drop) (2) | 2023.12.19 |
외부파일 드래그 앤 드롭1(File Drag and Drop) (3) | 2023.12.18 |
Form(폼) Serialize 속 살짝 들여다 보깅! (2) | 2023.11.24 |