보라코딩
스프링 파일 업로드 (ajax) 본문
기본 설정
web.xml
위에 두번째줄부터 2.5인 부분 3.1으로 변경
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
servlet내에 태그 추가
<!-- 파일업로드 설정 시작 -->
<multipart-config>
<location>C:\\upload</location> <!-- 업로드 되는 파일을 저장할 공간 -->
<max-file-size>20971520</max-file-size> <!-- 최대크기 1MB * 20 -->
<max-request-size>41943040</max-request-size> <!-- 한번에 올릴 수 있는 최대크기 1MB * 40 -->
<file-size-threshold>20971520</file-size-threshold> <!-- 메모리 사용 1MB * 20 -->
</multipart-config>
<!-- 파일업로드 설정 끝 -->
servlet-context.xml
<!-- 첨부파일을 처리하는 빈 설정 -->
<beans:bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver">
</beans:bean>
pom.xml
<!-- https://mvnrepository.com/artifact/net.coobird/thumbnailator -->
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.6</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.6</version>
</dependency>
uploadForm.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="uploadFormAction" method="post" enctype="multipart/form-data">
<input type='file' name='uploadFile' multiple>
<button>Submit</button>
</form>
</body>
</html>
ajax방법
Controller
package com.mystudy.spring2;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.net.URLDecoder;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import com.mystudy.domain.AttachFileDTO;
import lombok.extern.log4j.Log4j;
import net.coobird.thumbnailator.Thumbnailator;
@Controller
@Log4j
public class UploadController {
@GetMapping("/uploadForm")
public void uploadForm() {
log.info("upload form");
}
@PostMapping("/uploadFormAction")
public void uploadFormPost(MultipartFile[] uploadFile, Model model) {
String uploadFolder = "C:\\upload";
for (MultipartFile multipartFile : uploadFile) {
log.info("===========================");
log.info("upload file name : " + multipartFile.getOriginalFilename());
log.info("upload file size : " + multipartFile.getSize());
File saveFile = new File(uploadFolder, multipartFile.getOriginalFilename());
try {
multipartFile.transferTo(saveFile);
}catch (Exception e) {
log.error(e.getMessage());
}
}
}
//ajax
@GetMapping("/uploadAjax")
public void uploadAjax() {
log.info("upload ajax");
}
@PostMapping(value = "/uploadAjaxAction",
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public ResponseEntity<List<AttachFileDTO>> uploadAjaxPost(MultipartFile[] uploadFile) {
List<AttachFileDTO> list = new ArrayList<>();
String uploadFolder = "C:\\upload";
String uploadFolderPath = getFolder();
// make folder -----------------------
File uploadPath = new File(uploadFolder, getFolder());
log.info("upload path: " + uploadPath);
if (uploadPath.exists() == false) {
uploadPath.mkdirs();
}
for (MultipartFile multipartFile : uploadFile) {
//log.info("-------------------------------");
//log.info("upload file name : " + multipartFile.getOriginalFilename());
//log.info("upload file size : " + multipartFile.getSize());
AttachFileDTO attachFileDTO = new AttachFileDTO();
String uploadFileName = multipartFile.getOriginalFilename();
//IE has file path
uploadFileName = uploadFileName.substring(uploadFileName.lastIndexOf("\\") + 1);
log.info("only file name : " + uploadFileName);
attachFileDTO.setFileName(uploadFileName);
UUID uuid = UUID.randomUUID();
uploadFileName = uuid.toString() + "_" + uploadFileName;
try {
File saveFile = new File(uploadPath, uploadFileName);
multipartFile.transferTo(saveFile);
attachFileDTO.setUuid(uuid.toString());
attachFileDTO.setUploadPath(uploadFolderPath);
//check image type file
if (checkImageType(saveFile)) {
attachFileDTO.setImage(true);
FileOutputStream thumbnail = new FileOutputStream(new File(uploadPath, "s_" + uploadFileName));
Thumbnailator.createThumbnail(multipartFile.getInputStream(), thumbnail, 100, 100);
thumbnail.close();
}
//add to List
list.add(attachFileDTO);
} catch (Exception e) {
log.error(e.getMessage());
}
}
return new ResponseEntity<List<AttachFileDTO>>(list, HttpStatus.OK);
}
@GetMapping("/display")
@ResponseBody
public ResponseEntity<byte[]> getFile(String fileName) {
log.info("fileName: " + fileName);
File file = new File("c:\\upload\\" + fileName);
log.info("file: " + file);
ResponseEntity<byte[]> result = null;
try {
HttpHeaders header = new HttpHeaders();
header.add("Content-Type", Files.probeContentType(file.toPath()));
result = new ResponseEntity<>(FileCopyUtils.copyToByteArray(file), header, HttpStatus.OK);
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
@GetMapping(value = "/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
@ResponseBody
public ResponseEntity<Resource> downloadFile(@RequestHeader("User-Agent") String userAgent, String fileName) {
Resource resource = new FileSystemResource("c:\\upload\\" + fileName);
if (resource.exists() == false) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
String resourceName = resource.getFilename();
// remove UUID
String resourceOriginalName = resourceName.substring(resourceName.indexOf("_") + 1);
HttpHeaders headers = new HttpHeaders();
try {
String downloadName = null;
if ( userAgent.contains("Trident")) {
log.info("IE browser");
downloadName = URLEncoder.encode(resourceOriginalName, "UTF-8").replaceAll("\\+", " ");
}else if(userAgent.contains("Edge")) {
log.info("Edge browser");
downloadName = URLEncoder.encode(resourceOriginalName,"UTF-8");
}else {
log.info("Chrome browser");
downloadName = new String(resourceOriginalName.getBytes("UTF-8"), "ISO-8859-1");
}
log.info("downloadName: " + downloadName);
headers.add("Content-Disposition", "attachment; filename=" + downloadName);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return new ResponseEntity<Resource>(resource, headers, HttpStatus.OK);
}
@PostMapping("/deleteFile")
@ResponseBody
public ResponseEntity<String> deleteFile(String fileName, String type) {
log.info("deleteFile: " + fileName);
File file;
try {
file = new File("c:\\upload\\" + URLDecoder.decode(fileName, "UTF-8"));
file.delete();
if (type.equals("image")) {
String largeFileName = file.getAbsolutePath().replace("s_", "");
log.info("largeFileName: " + largeFileName);
file = new File(largeFileName);
file.delete();
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<String>("deleted", HttpStatus.OK);
}
private String getFolder() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = new Date();
String str = sdf.format(date);
return str.replace("-", File.separator);
}
private boolean checkImageType(File file) {
try {
String contentType = Files.probeContentType(file.toPath());
return contentType.startsWith("image");
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
}
uploadAjax.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Upload with Ajax</h1>
<style>
.uploadResult {
width: 100%;
background-color: gray;
}
.uploadResult ul {
display: flex;
flex-flow: row;
justify-content: center;
align-items: center;
}
.uploadResult ul li {
list-style: none;
padding: 10px;
}
.uploadResult ul li img {
width: 100px;
}
.bigPictureWrapper {
position: absolute;
display: none;
justify-content: center;
align-items: center;
top:0%;
width:100%;
height:100%;
background-color: gray;
z-index: 100;
}
.bigPicture {
position: relative;
display:flex;
justify-content: center;
align-items: center;
}
</style>
<div class='bigPictureWrapper'>
<div class='bigPicture'>
</div>
</div>
<div class='uploadDiv'>
<input type='file' name='uploadFile' multiple>
</div>
<div class='uploadResult'>
<ul>
</ul>
</div>
<button id='uploadBtn'>Upload</button>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script>
function showImage(fileCallPath){
//alert(fileCallPath);
$(".bigPictureWrapper").css("display","flex").show();
$(".bigPicture")
.html("<img src='/display?fileName="+ encodeURI(fileCallPath)+"'>")
.animate({width:'100%', height: '100%'}, 1000);
}
$(".bigPictureWrapper").on("click", function(e){
$(".bigPicture").animate({width:'0%', height: '0%'}, 1000);
/* setTimeout(() => {
$(this).hide();
}, 1000); */
setTimeout(function() {
$(".bigPicture").hide();
}, 1000);
});
$(".uploadResult").on("click","span", function(e){
var targetFile = $(this).data("file");
var type = $(this).data("type");
console.log(targetFile);
$.ajax({
url: '/deleteFile',
data: {fileName: targetFile, type:type},
dataType:'text',
type: 'POST',
success: function(result){
alert(result);
}
}); //$.ajax
});
var regex = new RegExp("(.*?)\.(exe|sh|zip|alz)$");
var maxSize = 5242880; //5MB
function checkExtension(fileName, fileSize) {
if (fileSize >= maxSize) {
alert("파일 사이즈 초과");
return false;
}
if (regex.test(fileName)) {
alert("해당 종류의 파일은 업로드할 수 없습니다.");
return false;
}
return true;
}
var cloneObj = $(".uploadDiv").clone();
$("#uploadBtn").on("click", function(e) {
var formData = new FormData();
var formData = new FormData();
var inputFile = $("input[name='uploadFile']");
var files = inputFile[0].files;
//console.log(files);
for (var i = 0; i < files.length; i++) {
if (!checkExtension(files[i].name, files[i].size)) {
return false;
}
formData.append("uploadFile", files[i]);
}
$.ajax({
url : '/uploadAjaxAction',
processData : false,
contentType : false,
data : formData,
type : 'POST',
dataType : 'json',
success : function(result) {
console.log(result);
showUploadedFile(result);
$(".uploadDiv").html(cloneObj.html());
}
}); //$.ajax
});
var uploadResult = $(".uploadResult ul");
function showUploadedFile(uploadResultArr){
var str = "";
$(uploadResultArr).each(function(i, obj){
if(!obj.image){
var fileCallPath = encodeURIComponent( obj.uploadPath+"/"+ obj.uuid +"_"+obj.fileName);
var fileLink = fileCallPath.replace(new RegExp(/\\/g),"/");
str += "<li><div><a href='/download?fileName="+fileCallPath+"'>"+
"<img src='/resources/img/attach.png'>"+obj.fileName+"</a>"+
"<span data-file=\'"+fileCallPath+"\' data-type='file'> x </span>"+
"<div></li>"
}else{
var fileCallPath = encodeURIComponent( obj.uploadPath+ "/s_"+obj.uuid +"_"+obj.fileName);
var originPath = obj.uploadPath+ "\\"+obj.uuid +"_"+obj.fileName;
originPath = originPath.replace(new RegExp(/\\/g),"/");
str += "<li><a href=\"javascript:showImage(\'"+originPath+"\')\">"+
"<img src='display?fileName="+fileCallPath+"'></a>"+
"<span data-file=\'"+fileCallPath+"\' data-type='image'> x </span>"+
"<li>";
}
});
uploadResult.append(str);
}
</script>
</body>
</html>
파일 업로드에서 고려해야 하는 것들
- 동일한 이름으로 파일 업로드 되었을때 기존 파일 사라지는 문제
- 이미지 파일의 경우에는 원본파일의 용량이 큰 경우 섬네일 이미지를 생성해야 하는 문제
- 이미지 파일과 일반 파일을 구분해서 다운로드 혹은 페이지에서 조회하도록 처리하는 문제
- 첨부파일 공격에 대비하기 위한 업로드 파일의 확장자 제한
'코딩 > Spring' 카테고리의 다른 글
스프링 시큐리티 CSRF 토큰, 로그아웃 (0) | 2023.05.15 |
---|---|
스프링 시큐리티 기본 설정, 로그인 (0) | 2023.05.15 |
페이징처리, 검색 처리 SQL (0) | 2023.05.12 |
RestController test (MockMVC) (0) | 2023.05.12 |
스프링 게시판 CRUD view (.jsp) (0) | 2023.05.12 |