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_filesapplication.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 | 
 
										
									 
										
									 
										
									 
										
									
댓글