실습(댓글)
- ex03
1. pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>ex03</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ex03</name>
<description>attachment</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- JSON -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
2. src/main/resource/application.properties
#server port
server.port=10004
#multipart
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=40MB
spring.servlet.multipart.max-request-size=100MB
spring.servlet.multipart.file-size-threshold=100MB
3. src/main/java/com.example.ex03/domain/vo/AttachFileVO.java
package com.example.ex03.domain.vo;
import lombok.Data;
import org.springframework.stereotype.Component;
@Component
@Data
public class AttachFileVO {
private String fileName;
private String originalFileName;
private String uploadDirectory;
private boolean image;
}
4. src/main/resource/templates/upload/uploadForm.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>파일 업로드(form태그)</title>
</head>
<body>
<form action="/upload/uploadForm" method="post" enctype="multipart/form-data">
<input type="file" name="files" multiple>
<button>Submit</button>
</form>
</body>
</html>
5. src/main/resource/templates/upload/uploadAjax.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>파일 업로드(Ajax)</title>
<style>
.result {
width: 100%;
}
.result ul {
display: flex;
justify-content: center;
}
.result ul li {
list-style: none;
padding: 15px;
}
</style>
</head>
<body>
<div class="upload">
<input type="file" name="files" multiple>
</div>
<div class="result">
<ul></ul>
</div>
<button id="upload">Submit</button>
</body>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
let regex = new RegExp("(.*?)\.(exe|sh|zip|alz)$");
let maxSize = 5242880; // 5MB
const result = $("div.result ul");
function checkExtension(fileName, fileSize){
if(regex.test(fileName)){
alert("(" + fileName + ")업로드 할 수 없는 파일의 형식입니다.")
return false;
}
if(fileSize >= maxSize){
alert("(" + fileName + ")파일 사이즈 초과")
return false;
}
return true;
}
$("#upload").on("click", function(e){
let formData = new FormData();
let input = $("input[name='files']");
let files = input[0].files;
// console.log(files);
for(let i=0; i<files.length; i++){
if(checkExtension(files[i].name, files[i].size)){
formData.append("files", files[i]);
}
}
$.ajax({
url: "uploadAjax",
type: "post",
data: formData,
processData: false,
contentType: false,
success: function(fileList){
showUploadFile(fileList);
}
});
});
function showUploadFile(fileList){
let str = "";
$.each(fileList, function(i, file){
str += "<li><a href='/upload/download?path=" + file.uploadDirectory + "/" + file.fileName + "'>";
str += file.image ? "<img src='/upload/display?path=" + file.uploadDirectory + "/t_" + file.fileName + "'>"
: "<img src='/images/attach.png' width='100'>";
str += "</a>" + file.originalFileName;
str += "<span data-path='" + file.uploadDirectory + "/t_" + file.fileName + "' style='cursor: pointer'>x</span>"
str += "</li>";
});
result.append(str);
}
$(".result").on("click", "span", function(e){
let path = $(this).data("path");
let li = $(this).closest("li");
$.ajax({
url: "delete",
type: "delete",
data: {path: path},
success: function(){
li.remove();
}
});
});
</script>
</html>
6. src/main/java/com.example.ex03/controller/UploadController.java
package com.example.ex03.controller;
import com.example.ex03.domain.vo.AttachFileVO;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnailator;
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.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
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;
// 1. 동일한 이름으로 파일이 업로드 되면, 기존 파일 삭제
// 2. 이미지 파일의 경우 원본 파일의 용량이 클 수 있기 때문에 썸네일 이미지가 필요하다.
// 3. 첨부파일 공격에 대비하기 위한 업로드 파일의 확장자 제한
@Controller
@Slf4j
@RequestMapping("/upload/*")
public class UploadController {
// form태그를 사용한 파일 업로드
@GetMapping("/uploadForm")
public void uploadForm(){
log.info("upload form");
}
@PostMapping("/uploadForm")
public void uploadForm(MultipartFile[] files) throws IOException {
String rootDirectory = "C:/upload";
for (MultipartFile file : files){
log.info("------------------------------------");
log.info("upload file name : " + file.getOriginalFilename());
log.info("upload file size : " + file.getSize());
File saveFile = new File(rootDirectory, file.getOriginalFilename());
file.transferTo(saveFile);
}
}
// ajax를 사용한 파일 업로드
@GetMapping("/uploadAjax")
public void uploadAjax(){
log.info("upload ajax");
}
@ResponseBody //REST
@PostMapping("/uploadAjax")
public List<AttachFileVO> uploadAjax(MultipartFile[] files) throws IOException{
List<AttachFileVO> fileList = new ArrayList<>();
String rootDirectory = "C:/upload";
File uploadDirectory = new File(rootDirectory, getDateDirectory());
if(!uploadDirectory.exists()) {uploadDirectory.mkdirs();}
for (MultipartFile file : files){
log.info("------------------------------------");
log.info("upload file name : " + file.getOriginalFilename());
log.info("upload file size : " + file.getSize());
AttachFileVO attachFileVO = new AttachFileVO();
UUID uuid = UUID.randomUUID();
String fileName = uuid.toString() + "_" + file.getOriginalFilename();
attachFileVO.setOriginalFileName(file.getOriginalFilename());
attachFileVO.setFileName(fileName);
attachFileVO.setUploadDirectory(getDateDirectory());
File saveFile = new File(uploadDirectory, fileName);
file.transferTo(saveFile);
if(checkImageType(saveFile)){
FileOutputStream thumbnail = new FileOutputStream(new File(uploadDirectory, "t_" + fileName));
// MultipartFile객체를 통해 바로 파일을 가져올 경우,
// 임시로 저장될 영역을 임계 영역이라 한다.
// apllication.properties에서 임계 영역에 대한 용량을 설정해 주어야
// 그 영역에 먼저 업로드 후 inputStream()을 가져올 수 있다.
Thumbnailator.createThumbnail(file.getInputStream(), thumbnail, 100, 100);
thumbnail.close();
attachFileVO.setImage(true);
}
fileList.add(attachFileVO);
}
return fileList;
}
@GetMapping("display")
@ResponseBody
public byte[] getFile(String path) throws IOException {
return FileCopyUtils.copyToByteArray(new File("C:/upload/" + path));
}
@GetMapping("/download")
@ResponseBody
public ResponseEntity<Resource> download(String path) throws UnsupportedEncodingException {
Resource resource = new FileSystemResource("C:/upload/" + path);
String name = resource.getFilename();
name = name.substring(name.indexOf("_") + 1);
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "attachment; filename=" + new String(name.getBytes("UTF-8"), "ISO-8859-1"));
return new ResponseEntity<Resource>(resource, headers, HttpStatus.OK);
}
@DeleteMapping("/delete")
@ResponseBody
public void deleteFile(String path){
// 썸네일 삭제
File file = new File("C:/upload", path);
if(file.exists()) {file.delete();}
// 원본파일 삭제
file = new File(file.getPath().replace("t_", ""));
if(file.exists()) {file.delete();}
}
private String getDateDirectory(){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
Date date = new Date();
String directory = sdf.format(date);
return directory;
}
private boolean checkImageType(File file) throws IOException{
return Files.probeContentType(file.toPath()).startsWith("image");
}
}
'웹 개발 > Spring' 카테고리의 다른 글
[Web_Spring] 22 (0) | 2022.07.04 |
---|---|
[Web_Spring] 21 (0) | 2022.07.03 |
[Web_Spring] 23 (0) | 2022.07.01 |
[Web_Spring] 19 (0) | 2022.07.01 |
[Web_Spring] 18 (0) | 2022.06.30 |