기능정리
더보기
- 일반 파일과 이미지파일을 따로 처리한다.
- 일반파일은 파일명으로 표시하고 이미지파일은 썸네일로 표시한다.
- 여러개의 파일을 업로드 한다.
- 한번에 여러개의 파일을 선택할 수 있다.
- 추가로 파일을 선택할 수 있다.
- 등록했던 파일을 삭제할 수 있다.
- 파일이름이 같은 경우 중복해서 등록할 수 없다.
- 일괄등록. 모든 파일을 선택한 후에 서버에 전송한다.
- ajax로 파일 업로드한다.
리팩터링 기준
더보기
- depth를 3이 넘지 않도록 구현.
- 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들기. ( 길이가 15라인을 넘어가지 않게 한다. )
- else 예약어를 쓰지 않기. ( if 조건절에서 값을 return 하도록 구현하면 else를 사용하지 않아도 된다.)
- (참조) https://zzang9ha.tistory.com/307
HTML
더보기
- 일반 파일 업로드 버튼과 이미지파일 업로드 버튼이 2가지가 있다.
<div id="uploadFilesBox" class="mb-3">
<label for="uploadFiles" class="btn">파일 여러개 업로드</label>
<input type="file" multiple="multiple" id="uploadFiles" name="uploadFiles" style="display:none;" />
</div>
<br>
<div id="uploadImagesBox" class="mb-3">
<label for="uploadImages" class="btn" >이미지 업로드</label>
<input type="file" multiple="multiple" id="uploadImages" name="uploadImages" style="display:none;" >
</div>
<br>
<input type="button" onclick="fileUploadFormSubmit();" value="등록" />
파일 업로드 버튼에 이벤트 등록
더보기
- 파일 업로드 버튼 id 값과 컨테이너 태그의 id값을 인자값으로 전달한다.
KurtFile.addEventFileTagAppend( "uploadFiles" , "uploadFilesBox" );
KurtFile.addEventFileTagAppend( "uploadImages" , "uploadImagesBox" );
File Class 로직
더보기
- 파일 태그에 변경이 감지되면 실행된다.
- 기존 등록된 파일 검증.
- 버튼 종류에 따라 일반 파일 또는 이미지 파일 로직 실행.
- 삭제 버튼 및 삭제 이벤트 추가.
- ajax 업로드용 fileList 객체 관리.
/* File 제어 공통 */
let KurtFile = {
uploadFiles : [] ,
uploadImages : [] ,
};
KurtFile.addEventFileTagAppend = (fileTagId , fileContainerId ) => {
const fileVo = {
fileContainerId : fileContainerId ,
fileTagId : fileTagId ,
$file : document.getElementById(fileTagId) ,
fileName : ( fileTagId == "uploadFiles" )? "uploadFiles" : "uploadImages" ,
fileBoxId : ( fileTagId == "uploadFiles" )? "uploadFileBox" : "uploadImageBox" ,
fileBoxNum : 0 ,
paramFileList : ( fileTagId == "uploadFiles" )? KurtFile.uploadFiles : KurtFile.uploadImages ,
isUploadFiles : fileTagId == 'uploadFiles' ,
};
fileVo.$file.onchange = () => {
[...fileVo.$file.files].forEach( (file , index )=>{
if( isSameParamFile(file) ) return;
( fileVo.isUploadFiles )? fileAppend(file) : imageAppend(file);
} );
fileVo.$file.value = "";
};
const isSameParamFile = (file) =>{
const result = fileVo.paramFileList.find(paramFile =>{ return paramFile.name == file.name }) ;
if( result != undefined ) alert(`${file.name}는 업로드된 파일이거나 파일 이름이 같습니다.`);
return result != undefined;
}
/* File Append 로직 */
const fileAppend = ( file ) => {
const $fileBox = fileBoxTagMake();
KurtFile.uploadFiles.push(file);
$fileBox.append( makeFileShowTag(file) );
const $fileDelete = deleteTag(fileVo.fileName);
addEventRemoveFile( $fileDelete , file , $fileBox.getAttribute("id") );
$fileBox.append( $fileDelete );
fileBoxTagAppend( $fileBox );
};
const makeFileShowTag = (file) => {
const $div = document.createElement("div");
$div.textContent = file.name;
return $div;
};
/* image Append 로직 */
const imageAppend = ( file ) => {
let fileReader = reader(file);
fileReader.onload = (e) => {
const fileReader = e.target;
const $fileBox = fileBoxTagMake();
KurtFile.uploadImages.push(file);
$fileBox.append(makeImgShowTag(fileReader.result));
const $imageDelete = deleteTag(fileVo.fileName);
addEventRemoveFile( $imageDelete , file , $fileBox.getAttribute("id") );
$fileBox.append($imageDelete);
fileBoxTagAppend( $fileBox );
}
};
const reader = (file) => {
const reader = new FileReader();
reader.readAsDataURL(file);
return reader;
};
const makeImgShowTag = (fileSrc) => {
const $img = document.createElement("img");
$img.setAttribute("class","imageShow");
$img.src = fileSrc;
return $img;
};
/* 공통 로직 */
const fileBoxTagMake = () => {
const $fileBox = document.createElement("div");
$fileBox.setAttribute("id" , fileVo.fileBoxId+fileVo.fileBoxNum++);
$fileBox.setAttribute("class" , 'fileBox');
return $fileBox;
}
const fileBoxTagAppend = ($fileBoxTag) => {
const $fileContainer = document.getElementById(fileVo.fileContainerId);
$fileContainer.append($fileBoxTag);
};
const deleteTag = ( fileName , fileBoxId ) => {
const $fileDelete = document.createElement("img");
$fileDelete.setAttribute("class",`${fileName}_remove_btn remove_btn` );
return $fileDelete;
};
const addEventRemoveFile = ( $fileDelete , file , fileBoxId )=>{
$fileDelete.addEventListener('click',()=>{
deleteParamFile(file);
deleteFileBox(fileBoxId);
});
};
const deleteParamFile = (file) => {
fileVo.paramFileList.forEach((paramFile,index)=>{
if( paramFile.name == file.name ) fileVo.paramFileList.splice(index , 1);
});
}
const deleteFileBox = (fileBoxId) => {
const $fileBox = document.getElementById(fileBoxId);
$fileBox.remove();
}
}
파일 업로드 전송 로직
더보기
fileList를 ajax로 전송한다.
function fileUploadFormSubmit(){
const form = new FormData();
// form.append( "title", document.getElementById('title').value );
// form.append( "content", document.getElementById('content').value );
KurtFile.uploadFiles.forEach((file)=>{ form.append( "uploadFiles", file ); });
KurtFile.uploadImages.forEach((file)=>{ form.append( "uploadImages", file ); });
const url = new URL(window.location.href);
const urlParams = url.searchParams;
const page = ( urlParams.get('page') )?? 1;
$.ajax({
url : `/boards?page=${page}`
, type : "POST"
, processData : false
, contentType : false
, data : form
, dataType : 'html'
, success:function(response) {
$('html').html(response);
}
});
}
테스트용 전체 코드 ( .html )
더보기
<!DOCTYPE html>
<html>
<head>
<style>
.fileBox { position : relative; width:100%; display: flex; }
.imageShow { width: 230px; }
.remove_btn{
background-image: url("https://w.namu.la/s/096ff2aacd1068d19f89d10f45bbf1db309d3f66b5d23c07ef113ab8a1ef72e8e1902b0b49ceb816c0b5cf894e82859704b9b059925448f42ac3c07dfcfafa1f011d5365a3e62c8da036f9d80cf76e4e8cba00f41fecf0a8a5bf82679f461b36");;
background-size: 30px; width:30px; height:30px; cursor:pointer;
}
.uploadFile_remove_btn { }
.uploadImage_remove_btn { position: absolute; top: 0px; left : 200px; }
.btn{
border: 1px solid #757575; background-color: #EEEEEE;
font-size: 12px; padding: 2px; cursor: pointer;
}
</style>
</head>
<body>
<div id="uploadFilesBox" class="mb-3">
<label for="uploadFiles" class="btn">파일 여러개 업로드</label>
<input type="file" multiple="multiple" id="uploadFiles" name="uploadFiles" style="display:none;" />
</div>
<br>
<div id="uploadImagesBox" class="mb-3">
<label for="uploadImages" class="btn" >이미지 업로드</label>
<input type="file" multiple="multiple" id="uploadImages" name="uploadImages" style="display:none;" >
</div>
<br>
<input type="button" onclick="fileUploadFormSubmit();" value="등록" />
<script>
/* File 제어 공통 */
let KurtFile = {
uploadFiles : [] ,
uploadImages : [] ,
};
KurtFile.addEventFileTagAppend = (fileTagId , fileContainerId ) => {
const fileVo = {
fileContainerId : fileContainerId ,
fileTagId : fileTagId ,
$file : document.getElementById(fileTagId) ,
fileName : ( fileTagId == "uploadFiles" )? "uploadFiles" : "uploadImages" ,
fileBoxId : ( fileTagId == "uploadFiles" )? "uploadFileBox" : "uploadImageBox" ,
fileBoxNum : 0 ,
paramFileList : ( fileTagId == "uploadFiles" )? KurtFile.uploadFiles : KurtFile.uploadImages ,
isUploadFiles : fileTagId == 'uploadFiles' ,
};
fileVo.$file.onchange = () => {
[...fileVo.$file.files].forEach( (file , index )=>{
if( isSameParamFile(file) ) return;
( fileVo.isUploadFiles )? fileAppend(file) : imageAppend(file);
} );
fileVo.$file.value = "";
};
const isSameParamFile = (file) =>{
const result = fileVo.paramFileList.find(paramFile =>{ return paramFile.name == file.name }) ;
if( result != undefined ) alert(`${file.name}는 업로드된 파일이거나 파일 이름이 같습니다.`);
return result != undefined;
}
/* File Append 로직 */
const fileAppend = ( file ) => {
const $fileBox = fileBoxTagMake();
KurtFile.uploadFiles.push(file);
$fileBox.append( makeFileShowTag(file) );
const $fileDelete = deleteTag(fileVo.fileName);
addEventRemoveFile( $fileDelete , file , $fileBox.getAttribute("id") );
$fileBox.append( $fileDelete );
fileBoxTagAppend( $fileBox );
};
const makeFileShowTag = (file) => {
const $div = document.createElement("div");
$div.textContent = file.name;
return $div;
};
/* image Append 로직 */
const imageAppend = ( file ) => {
let fileReader = reader(file);
fileReader.onload = (e) => {
const fileReader = e.target;
const $fileBox = fileBoxTagMake();
KurtFile.uploadImages.push(file);
$fileBox.append(makeImgShowTag(fileReader.result));
const $imageDelete = deleteTag(fileVo.fileName);
addEventRemoveFile( $imageDelete , file , $fileBox.getAttribute("id") );
$fileBox.append($imageDelete);
fileBoxTagAppend( $fileBox );
}
};
const reader = (file) => {
const reader = new FileReader();
reader.readAsDataURL(file);
return reader;
};
const makeImgShowTag = (fileSrc) => {
const $img = document.createElement("img");
$img.setAttribute("class","imageShow");
$img.src = fileSrc;
return $img;
};
/* 공통 로직 */
const fileBoxTagMake = () => {
const $fileBox = document.createElement("div");
$fileBox.setAttribute("id" , fileVo.fileBoxId+fileVo.fileBoxNum++);
$fileBox.setAttribute("class" , 'fileBox');
return $fileBox;
}
const fileBoxTagAppend = ($fileBoxTag) => {
const $fileContainer = document.getElementById(fileVo.fileContainerId);
$fileContainer.append($fileBoxTag);
};
const deleteTag = ( fileName , fileBoxId ) => {
const $fileDelete = document.createElement("img");
$fileDelete.setAttribute("class",`${fileName}_remove_btn remove_btn` );
return $fileDelete;
};
const addEventRemoveFile = ( $fileDelete , file , fileBoxId )=>{
$fileDelete.addEventListener('click',()=>{
deleteParamFile(file);
deleteFileBox(fileBoxId);
});
};
const deleteParamFile = (file) => {
fileVo.paramFileList.forEach((paramFile,index)=>{
if( paramFile.name == file.name ) fileVo.paramFileList.splice(index , 1);
});
}
const deleteFileBox = (fileBoxId) => {
const $fileBox = document.getElementById(fileBoxId);
$fileBox.remove();
}
}
function fileUploadFormSubmit(){
const form = new FormData();
form.append( "title", document.getElementById('title').value );
form.append( "content", document.getElementById('content').value );
KurtFile.uploadFiles.forEach((file)=>{ form.append( "uploadFiles", file ); });
KurtFile.uploadImages.forEach((file)=>{ form.append( "uploadImages", file ); });
const url = new URL(window.location.href);
const urlParams = url.searchParams;
const page = ( urlParams.get('page') )?? 1;
$.ajax({
url : `/boards?page=${page}`
, type : "POST"
, processData : false
, contentType : false
, data : form
, dataType : 'html'
, success:function(response) {
$('html').html(response);
}
});
}
KurtFile.addEventFileTagAppend( "uploadFiles" , "uploadFilesBox" );
KurtFile.addEventFileTagAppend( "uploadImages" , "uploadImagesBox" );
</script>
</body>
</html>
반응형
'Javascript' 카테고리의 다른 글
[ Javascript ] getElement와 querySelector의 차이. (HTLMCollection과 NodeList의 차이) (0) | 2022.07.11 |
---|---|
[ Sweetalert ] (0) | 2021.11.20 |
[ Javascript ] (0) | 2021.11.16 |