jongviet

June 30, 2021 - Spring 파일업로드 본문

Spring legacy

June 30, 2021 - Spring 파일업로드

jongviet 2021. 6. 30. 22:54

*6월30일

-파일업로드에 대해 포스팅해보자.

 

파일업로드

-파일은 수정보다 통째로 올리고 통째로 지우는게 더 깔끔함.

 

*imports

org.springframework.web.multipart.MultipartFile

org.apache.tika.Tika

net.coobird.thumbnailator.Thumbnails

java.util.UUID.... 등등

 

*세팅 파일 업데이트

-web.xml

 

<multipart-config>
<location>C:\\Spring\\workspace\\upload</location>
<!-- 파일관련 추가 -->
<max-file-size>1048576</max-file-size> <!-- 1mb제한, 파일 하나당; byte 기준 -->
<max-request-size>10485760</max-request-size> <!-- 10개 파일 전체 사이즈 -->
<file-size-threshold>1048576</file-size-threshold> <!-- 검증할 사이즈 -->
</multipart-config>

 

-servlet-context.xml

 

<!-- mapping은 내가 쓰는 코드상의 주소체계 / location은 실제 위치 값 / 'file://' -->
<resources mapping="/upload/**" location="file:///C:/Spring/workspace/upload/"></resources>       

 

<!-- 멀티파트는 별도 멀티파트 리졸버가 있어야 처리 가능 -->
<beans:bean id="multipartResolver"
      class="org.springframework.web.multipart.support.StandardServletMultipartResolver">
   </beans:bean>

 

<context:component-scan base-package="com.myweb.orm"/>

 

*view단

 

<form action="/product/register" method="post"
enctype="multipart/form-data"//폼태그 enctype 변경

 

<div class="form-group">
<input type="file" class="form-control" id="files" name="files"
multiple style="display: none;">
<!-- 파일 다중 선택 가능 옵션 : multiple -->
<button type="button" class="btn btn-outline-info btn-block"
id="fileTrigger">파일업로드</button>
</div>
<div class="form-group">
<ul class="list-group" id="fileZone"></ul>
</div>

 

<script>
$(document).on("click", "#fileTrigger", function() {
   $("#files").click(); //부트스트랩 버튼 사용
});

let regExp = new RegExp("\.(exe|sh|bat|js|msi|dll)$"); //정규식 객체 생성; 
let maxSize = 1048576; // 1MB

function fileValidation(fname, fsize){
   if(regExp.test(fname)){   //지정한 파일 형식 필터링
      alert(fname + "는 허용되지 않는 파일 형식입니다!");
      return false;
   }else if(fsize > maxSize){
      alert("1MB 이하의 파일만 허용됩니다!");
      return false;
   }else{
      return true;
   }
   
}
$(document).on("change", "#files", function() {
   $("button[type=submit]").attr("disabled", false);
   let formObj = $("#files");
   let fileObjs = formObj[0].files;  //업로드한 파일 전체 리스트 담김 
   let fileZone = $("#fileZone");
   fileZone.html("");

 

console.log(formObj[0].files)


  
   for (let fobj of fileObjs) {
      let li = '<li class="list-group-item d-flex justify-content-between align-items-center">';
      if(fileValidation(fobj.name, fobj.size)) //업로드 파일 검증 작업
         // 정상출력
         li += fobj.name + '<span class="badge badge-success badge-pill">';
      }else{
         // 크기나 타입 통과하지 않을 시 
         li += '<i class="fa fa-times-rectangle" style="font-size:24px;color:red"></i>';
         li += fobj.name + '<span class="badge badge-danger badge-pill">';
         $("button[type=submit]").attr("disabled", true);
      }
      li += (fobj.size/1024/1024).toFixed(2) +'MB</span></li>';  //mb 처리
      fileZone.append(li);
   }
});
</script>

 

<!-- File List part!! -->
<c:if test="${pvo.flist.size() > 0 }">
<tr>
<td colspan="2">
<ul class="list-group" id="fileZone">
<c:forEach items="${pvo.flist}" var="fvo">
<li
class="list-group-item d-flex justify-content-between align-items-center">
<c:choose>
<c:when test="${fvo.ftype > 0}">
<img src="/upload/${fvo.savedir }/${fvo.uuid}_th_${fvo.fname}"> //썸네일 저장용; servlet-context.xml에서 잡은 경로 기준 하단부만
</c:when>
<c:otherwise>
<i class="fa fa-file-text-o"
style="font-size: 48px; color: red"></i>
</c:otherwise>
</c:choose> <a href="/upload/${fvo.savedir }/${fvo.uuid}_${fvo.fname}"> // 다운로드용 링크
<span class="badge badge-success badge-pill">${fvo.fname }</span>
</a>
</li>
</c:forEach>
</ul>
</td>
</tr>
</c:if>

 

*Controller

 

@RequestParam(name="files", required = false) MultipartFile[] files  //상품 등록 시, 배열형 multipart로 파일 접수

 

if(files[0].getSize() > 0) {
int pno = psv.getCurrPno();  //현재 pno
isUp = fp.upload_file(files, pno); //pno에 맞게 파일 업로드

}

 

*ORM/FileProcessor

 

@Inject
private FilesDAORule fdao;

public int upload_file(MultipartFile[] files, int pno) {
final String UP_DIR = "C:\\Spring\\workspace\\upload"; //파일 저장 경로
// 날짜별 폴더 경로 : upload/2021/06/28/uuid_fname.jpg => if 이미지 : uuid_th_fname.jpg

LocalDate date = LocalDate.now();
String today = date.toString(); //2021-06-28
today = today.replace("-", File.separator); //file.sesparator : '\\' -> 2021\\06\\28 //escape + 문자 '\'
File folder = new File(UP_DIR, today);  //파일 경로

if(!folder.exists()) folder.mkdirs(); //폴더 없을 시 생성

int isUp = 1;

for (MultipartFile f : files) {
FilesVO fvo = new FilesVO();
fvo.setSavedir(today);

String originalFileName = f.getOriginalFilename();
logger.info(">>>>>>> orginalFileName : " + originalFileName);

fvo.setFname(originalFileName);

UUID uuid = UUID.randomUUID(); //랜덤넘버; 강력함!
fvo.setUuid(uuid.toString());

String fullFileName = uuid.toString() + "_" + originalFileName;
File storeFile = new File(folder, fullFileName); //파일 경로 및 파일명

try {
f.transferTo(storeFile);  //파일 이동 및 저장
if(isImageFile(storeFile)) {
fvo.setFtype(1); //이미지는 1, 그외는 0
File thumbnail = new File(folder, uuid.toString() + "_th_" + originalFileName); //썸네일 객체 명칭
Thumbnails.of(storeFile).size(100, 100).toFile(thumbnail); //어떤경로의 어떤 파일을 어떠한 사이즈로 만들지~
}
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
fvo.setPno(pno);
isUp *= fdao.insert(fvo);  //하나라도 에러나면 0곱해져서 0 리턴
}
return isUp; //for문 바깥쪽에
}


private boolean isImageFile(File storeFile) {
try {
String mimeType = new Tika().detect(storeFile); //단순 확장자명이 아닌 실제 파일 타입 검증 'lib - tika-parssers 1.25'
return mimeType.startsWith("image") ? true : false;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}

public int deleteFile(String uuid) {
return fdao.delete(uuid);
}

 

*Service, DAO, DB는 일반적인 CRUD 구조와 같으므로 생략

Comments