728x90
현재 팀프로젝트 진행 중~
우리 팀 주제에는 파일 업로드 다운로드는 없지만 다른 팀은 많은지 알려주심
https://spring.io/guides/gs/uploading-files/
Spring Boot 친절하게 어떻게 하는지 알려주지만 왜케 어려운 거 ㅠㅠㅠ
Maven으로 진행함 git-hub에서 다운 받으면 코드 그대로 제공해주기 때문에 별도의 코드 작성은 없었다.
내가 아는 만큼 알 수 있는 만큼 분석한 것이기 때문에 정확하다고 확정할 수는 없다..ㅋㅋ
Application Class
package com.example.uploadingfiles;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import com.example.uploadingfiles.storage.StorageProperties;
import com.example.uploadingfiles.storage.StorageService;
@SpringBootApplication
@EnableConfigurationProperties(StorageProperties.class)
public class UploadingFilesApplication {
public static void main(String[] args) {
SpringApplication.run(UploadingFilesApplication.class, args);
}
@Bean
CommandLineRunner init(StorageService storageService) {
return (args) -> {
storageService.deleteAll();
storageService.init();
};
}
}
@EnableConfigurationProperties
스프링 부트 커스텀 설정 프로퍼티 클래스를 빈으로 등록할 수 있는 어노테이션
@EnableConfigurationProperties을 설정 클래스에 추가하고 @EnableConfigurationProperties의 값으로 @ConfigurationProperties를 적용한 클래스를 지정하면 된다. 이 경우 @EnableConfigurationProperties는 해당 클래스를 빈으로 등록하고 프로퍼티 값을 할당한다.
package com.example.uploadingfiles.storage;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("storage")
public class StorageProperties {
/**
* Folder location for storing files
* 부팅 될때 이 정보가 올라감
*/
private String location = "upload-dir";
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
}
File Upload Controller
package com.example.uploadingfiles;
import java.io.IOException;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.example.uploadingfiles.storage.StorageFileNotFoundException;
import com.example.uploadingfiles.storage.StorageService;
@Controller
public class FileUploadController {
private final StorageService storageService;
@Autowired
public FileUploadController(StorageService storageService) {
this.storageService = storageService;
}
/* loadAll() ; 생성 메서드 ; StorageService.java
* map() ; 제공 메서드 ; Stream.class
* MvcUriComponentsBuilder ; 제공 메서드 ; requestMapping 메서드
* 파일이 있으면 리스트로 보내주기 위해서 addAttribute에 file로 보냄
* */
@GetMapping("/")
public String listUploadedFiles(Model model) throws IOException {
model.addAttribute("files", storageService.loadAll().map(
path -> MvcUriComponentsBuilder.fromMethodName(FileUploadController.class,
"serveFile", path.getFileName().toString()).build().toUri().toString())
.collect(Collectors.toList()));
return "uploadForm";
}
/*파일 다운로드
* @ResponsBody ; 포워드 필요 없이 본인 스스로 응답하는 것
* @PathVariable ; {키 : + 값} ; get방식으로 받을때 ?로 받기 번거스러워서 사용하는 어노테이션
* ResponseEntity ; 스프링 제공 메서드 ; HttpEntity를 상속받음으로써 HttpHeader와 body를 가질 수 있다.
* */
@GetMapping("/files/{filename:.+}")
@ResponseBody
public ResponseEntity<Resource> serveFile(@PathVariable String filename) {
Resource file = storageService.loadAsResource(filename);
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + file.getFilename() + "\"").body(file);
}
/* 파일 업로드
* RequestParam으로 file을 받아서 MultipartFile 타입으로 file에 담는다.
* redirectAttributes는 post방식에서 리다이렉트 할 수 있음
* 피일이 올라가면 attribute에 성공했다고 메세지를 담아 보내쥼*/
@PostMapping("/")
public String handleFileUpload(@RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes) {
storageService.store(file);
redirectAttributes.addFlashAttribute("message",
"You successfully uploaded " + file.getOriginalFilename() + "!");
return "redirect:/";
}
@ExceptionHandler(StorageFileNotFoundException.class)
public ResponseEntity<?> handleStorageFileNotFound(StorageFileNotFoundException exc) {
return ResponseEntity.notFound().build();
}
}
File Upload Service
package com.example.uploadingfiles.storage;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
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.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
@Service
public class FileSystemStorageService implements StorageService {
private final Path rootLocation;
@Autowired
public FileSystemStorageService(StorageProperties properties) {
this.rootLocation = Paths.get(properties.getLocation());
}
@Override
public void store(MultipartFile file) {
/* 불필요한 패스 정리
* file에서 클라이어트 파일 시스템에 저장된 파일 이름을 가져와서 path 주소를 "path/.."으로 정리 해줌
* */
String filename = StringUtils.cleanPath(file.getOriginalFilename());
try {
if (file.isEmpty()) {
throw new StorageException("Failed to store empty file " + filename);
}
/* 그래서 여기 조건문에 ..이 들어감
* contains메서드는 boolean 리턴 타입 매개변수값이 맞다면 true를 반한
* */
if (filename.contains("..")) {
// This is a security check
throw new StorageException(
"Cannot store file with relative path outside current directory "
+ filename);
}
//결국 실행 부분은 여기
try (InputStream inputStream = file.getInputStream()) {
Files.copy(inputStream, this.rootLocation.resolve(filename),
StandardCopyOption.REPLACE_EXISTING); /* 옵션값 */
/* 여기서 중요한 거는 copy . 버전 마다 다르지만 앞으로 높은 버전은 일단 copy*/
}
}
catch (IOException e) {
throw new StorageException("Failed to store file " + filename, e);
}
}
/* walk ; 파일의 주소 중 시작 파일을 리턴 (시작파일, 몇번 째 파일) ; 이건가 본대
* relativize ; 현재 path와 주는 path 사이의 실제 path
* :: ; Applies this function to the given argument. 함수의 결과를 반환 (람다식이여서 함수라고 표현하나?)
* */
@Override
public Stream<Path> loadAll() {
try {
return Files.walk(this.rootLocation, 1)
.filter(path -> !path.equals(this.rootLocation))
.map(this.rootLocation::relativize);
}
catch (IOException e) {
throw new StorageException("Failed to read stored files", e);
}
}
@Override
public Path load(String filename) {
return rootLocation.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 StorageFileNotFoundException(
"Could not read file: " + filename);
}
}
catch (MalformedURLException e) {
throw new StorageFileNotFoundException("Could not read file: " + filename, e);
}
}
@Override
public void deleteAll() {
FileSystemUtils.deleteRecursively(rootLocation.toFile());
/* toFile로 파일 가져오고 Spring이 제공해주는 메서드 deleteRecursively로 제거*/
}
@Override
public void init() {
try {
Files.createDirectories(rootLocation);
}
catch (IOException e) {
throw new StorageException("Could not initialize storage", e);
}
}
}
서비스는 잘 모르겠엉 ㅠㅠ
반응형
'Backend > SpringBoot' 카테고리의 다른 글
에러 일기 (0) | 2020.07.21 |
---|---|
Spring Boot - 외부 API xml 방식으로 호출 하기 (0) | 2020.07.06 |
Spring Boot 리다이렉트하기 (0) | 2020.05.26 |
Get방식 parameter 넘기기 (0) | 2020.05.26 |
Baeldung (0) | 2020.05.26 |
댓글