Spring Boot
File Upload / Download / Delete / List
1. File Upload
file 관련 설정은
src/main/resources/application.yml ( 또는 application.properties ) 에 합니다.
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
location: d:\\temp\\spring_uploaded_files
application.properties 라면 spring.servlet.multipart.max-file-size=10MB 와 같이 쓰면 됩니다.
- spring.servlet.multipart.max-file-size : 총 파일 사이즈가 설정한 크기를 넘지 못합니다.
- spring.servlet.multipart.max-request-size : multipart/form-data 의 request 사이즈가 설정한 크기를 넘지 못합니다.
InfoController.java
// @ResponseBody 생략가능. class 에 @RestController
@PostMapping(value="uploadFile")
public ResponseEntity<String> uploadFile(MultipartFile file) throws IllegalStateException, IOException{
if( !file.isEmpty() ) {
log.debug("file org name = {}", file.getOriginalFilename());
log.debug("file content type = {}", file.getContentType());
file.transferTo(new File(file.getOriginalFilename()));
}
return new ResponseEntity<>("", HttpStatus.OK);
}
일단은 Controller 에서 file 관련된 내용을 보기위해 테스트해봤습니다.
form-data 의 KEY 와 메소드의 파라메터이름이 같아야 합니다.
로그 찍히고 파일이 저장되었습니다.
2021-07-11 13:31:30.771[DEBUG] : file org name = images.jpg
2021-07-11 13:31:30.771[DEBUG] : file content type = image/jpeg
이번엔 File 관련 서비스를 만들어서 처리하도록 하겠습니다.
StorageService
info package 아래에 storage package 를 만듭니다.
그 아래에 StorageService.java 를 interface 로 생성합니다.
StorageService.java
package com.bryan.hello.preword.info.storage;
import java.nio.file.Path;
import java.util.stream.Stream;
import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;
public interface StorageService {
void init();
void store(MultipartFile file);
Stream<Path> loadAll();
Path load(String filename);
Resource loadAsResource(String filename);
void deleteAll();
}
interface 를 구현해야겠죠. 같은 package 에
FileSystemStorageService.java 를 만듭니다. 일단 파일 저장하는 것 까지만,
package com.bryan.hello.preword.info.storage;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.stream.Stream;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
@Service
public class FileSystemStorageService implements StorageService {
@Value("${spring.servlet.multipart.location}")
private String uploadPath;
@Override
public void init() {
try {
Files.createDirectories(Paths.get(uploadPath));
} catch (IOException e) {
throw new RuntimeException("Could not create upload folder!");
}
}
@Override
public void store(MultipartFile file) {
try {
if (file.isEmpty()) {
throw new Exception("ERROR : File is empty.");
}
Path root = Paths.get(uploadPath);
if (!Files.exists(root)) {
init();
}
try (InputStream inputStream = file.getInputStream()) {
Files.copy(inputStream, root.resolve(file.getOriginalFilename()),
StandardCopyOption.REPLACE_EXISTING);
}
} catch (Exception e) {
throw new RuntimeException("Could not store the file. Error: " + e.getMessage());
}
}
@Override
public Stream<Path> loadAll() {
// TODO Auto-generated method stub
return null;
}
@Override
public Path load(String filename) {
// TODO Auto-generated method stub
return null;
}
@Override
public Resource loadAsResource(String filename) {
// TODO Auto-generated method stub
return null;
}
@Override
public void deleteAll() {
// TODO Auto-generated method stub
}
}
@Value 는 application.yml 의 값을 가져와서 주입해줍니다.
Controller 에서 StorageService 를 사용하기 위해 생성자 주입을 해줍니다.
Controller 에 PostMapping method 추가
@PostMapping(value="upload")
public ResponseEntity<String> upload(MultipartFile file) throws IllegalStateException, IOException{
storageService.store(file);
return new ResponseEntity<>("", HttpStatus.OK);
}
실행
결과는.. 잘 됩니다.
2. File Download
위에서 implement 했던 메소드 중에 load() 와 loadAsResource() 를 작성합니다.
FileSystemStorageService.java 의 load(), loadAsResource()
@Override
public Path load(String filename) {
return Paths.get(uploadPath).resolve(filename);
}
@Override
public Resource loadAsResource(String filename) {
try {
Path file = load(filename);
Resource resource = new UrlResource(file.toUri());
if (resource.exists() || resource.isReadable()) {
return resource;
}
else {
throw new RuntimeException("Could not read file: " + filename);
}
}
catch (MalformedURLException e) {
throw new RuntimeException("Could not read file: " + filename, e);
}
}
그리고 Controller 에서 다운로드 api 를 만듭니다.
InfoController.java 에 추가
// import 에 추가
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
// method 추가
@GetMapping(value="download")
public ResponseEntity<Resource> serveFile(@RequestParam(value="filename") String filename) {
Resource file = storageService.loadAsResource(filename);
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + file.getFilename() + "\"").body(file);
}
브라우저에서 호출해보기 쉽게 GET 방식으로 mapping 해줍니다.
실행
웹 브라우저에 아래와 같이 호출
localhost:8000/info/download?filename=images.jpg
그럼 file 이 다운로드 됩니다.
3. File Delete
전체 파일 삭제 입니다.
FileSystemStorageService.java 의 deleteAll() 을 구현해줍니다.
import org.springframework.util.FileSystemUtils;
@Override
public void deleteAll() {
FileSystemUtils.deleteRecursively(Paths.get(uploadPath).toFile());
}
컨트롤러에서 호출
@PostMapping(value="deleteAll")
public ResponseEntity<String> deleteAll(){
storageService.deleteAll();;
return new ResponseEntity<>("", HttpStatus.OK);
}
사용 전 주의하세요 !!
application.yml 에 설정한 location 이 삭제됩니다.
파일을 하나만 삭제하려면 StorageService Interface 에 추상 메소드delete() 를 추가하고
FileSystemStorageService.java 에 Override 해서 구현하고 사용하면 됩니다.
4. Files List
file들의 정보를 list 에 담아서 리턴해야 하기 때문에, FileData 라는 class 를 만들겠습니다.
model package 에 FileData.java
package com.bryan.hello.preword.info.model;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class FileData {
private String filename;
private String url;
private Long size;
}
FileSystemStorageService.java 의 loadAll() 을 구현해줍니다.
@Override
public Stream<Path> loadAll() {
try {
Path root = Paths.get(uploadPath);
return Files.walk(root, 1)
.filter(path -> !path.equals(root));
}
catch (IOException e) {
throw new RuntimeException("Failed to read stored files", e);
}
}
InfoController.java 에 추가
import java.util.stream.Collectors;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
@GetMapping("fileList")
public ResponseEntity<List<FileData>> getListFiles() {
List<FileData> fileInfos = storageService.loadAll()
.map(path ->{
FileData data = new FileData();
String filename = path.getFileName().toString();
data.setFilename(filename);
data.setUrl(MvcUriComponentsBuilder.fromMethodName(InfoController.class,
"serveFile", filename).build().toString());
try {
data.setSize(Files.size(path));
} catch (IOException e) {
log.error(e.getMessage());
}
return data;
})
.collect(Collectors.toList());
return ResponseEntity.status(HttpStatus.OK).body(fileInfos);
}
실행
http://localhost:8000/info/fileList
결과
Spring Boot Tutorial 시리즈
- 2021.07.03 - [Java] - [SpringBoot] RestApi 만들기 (1) 프로젝트 생성
- 2021.07.03 - [Java] - [SpringBoot] RestApi 만들기 (2) JSON 형식 리턴
- 2021.07.04 - [Java] - [SpringBoot] RestApi 만들기 (3) Log (slf4j+logback)
- 2021.07.04 - [Java] - [SpringBoot] RestApi 만들기 (4) Service 생성 (의존성 주입)
- 2021.07.04 - [Java] - [SpringBoot] RestApi 만들기 (5.1) MySQL + JDBC Template. 1
- 2021.07.05 - [Java] - [SpringBoot] RestApi 만들기 (5.2) DBCP - HikariCP
- 2021.07.08 - [Java] - [SpringBoot] RestApi 만들기 (5.3) jdbcTemplate - Select
- 2021.07.10 - [Java] - [SpringBoot] RestApi 만들기 (5.4) GetMapping, PostMapping
- 2021.07.10 - [Java] - [SpringBoot] RestApi 만들기 (5.5) jdbcTemplate - Insert, Update, Delete
Spring Boot Tutorial 부록
- 2021.07.02 - [Java] - [eclipse] 이클립스 설치, STS(Spring Tools 4) 설치
- 2021.07.04 - [Java] - [SpringBoot] MyBatis 보다 Spring JDBC 를 사용해야 하는 이유
- 2021.06.19 - [Java] - [Spring-boot] 시작하기 전 알아야 할 것들
- 2021.06.19 - [Java] - [Spring-boot] 수동으로 설정 초기화
- 2021.07.04 - [Java] - Eclipse 에 groovy 설치 하기
- 2021.07.02 - [Java] - [eclipse] 이클립스 설치, STS(Spring Tools 4) 설치
- 2021.07.07 - [Java] - [SpringBoot] Controller 에 Route 적용 (RequestMapping)
'Java' 카테고리의 다른 글
[eGovFrame] 전자정부 프레임워크 3.6 설치 및 테스트 실행 (0) | 2021.08.22 |
---|---|
[SpringBoot] Controller 에서 form-data 여러 파일과 json list string 같이 받기 (Converter) (2) | 2021.08.21 |
[SpringBoot] RestApi 만들기 (5.5) jdbcTemplate - Insert, Update, Delete (4) | 2021.07.10 |
[SpringBoot] RestApi 만들기 (5.4) GetMapping, PostMapping (0) | 2021.07.10 |
[SpringBoot] RestApi 만들기 (5.3) jdbcTemplate - Select (0) | 2021.07.08 |
댓글