Category: Spring Resource

0

Spring boot - StreamingResponseBody

목차 Spring boot - StreamingResponseBody Spring boot - ResourceRegion Spring boot - 파일 다운로드 서비스 구현하기 Spring boot - 파일 업로드 서비스 구현하기 Spring boot - Resource 추상화 Spring boot - MultipartFile 에서 발생하는 예외 처리 Spring boot - MultipartFile 를 이용한 파일 업로드 Spring boot - Part 객체를 이용한 파일 업로드 참고 https://www.baeldung.com/spring-mvc-sse-streams StreamingResponseBody Spring 에서 제공하는 비동기 요청 처리를 위한 객체, 응답값을 Byte 로 변환해 Streaming 형태로 줄 때 사용하는 객체 @GetMapping("/video3")public ResponseEntity<StreamingResponseBody> handleRbe() throws FileNotFoundException { File file = new File("/Users/dongwoo-yang/spring-file/mysong.mp4"); InputStream inputStream = new FileInputStream(file); StreamingResponseBody stream = out -> { byte[] data = new byte[1024]; int length = 0; while((length = inputStream.read(data)) >= 0){ out.write(data, 0, length); } inputStream.close(); out.flush(); }; HttpHeaders headers = new HttpHeaders(); headers.add("Content-Type", "video/mp4"); headers.add("Content-Length", Long.toString(file.length())); return ResponseEntity.ok().headers(headers).body(stream);}

0

Spring boot - ResourceRegion

목차 Spring boot - StreamingResponseBody Spring boot - ResourceRegion Spring boot - 파일 다운로드 서비스 구현하기 Spring boot - 파일 업로드 서비스 구현하기 Spring boot - Resource 추상화 Spring boot - MultipartFile 에서 발생하는 예외 처리 Spring boot - MultipartFile 를 이용한 파일 업로드 Spring boot - Part 객체를 이용한 파일 업로드 Http Range Request 서버에서 클라이언트로 HTTP 메시지 중 일부만 전송할 수 있도록 허용하는 기술 대용량의 미디어 파일, 파일 전송 중 일시 정지 및 다시 시작이 가능하다 Client 가 Range Header 를 통해 특정 리소스의 일부를 요청하면 서버가 그 부분만 전송하는 방식으로 동작한다. Server 가 Range Request 를 지원하면 Response Http Header 에 Content-Range 가 존재한다. HTTP Range 요청에 대한 정상 응답 코드로는 PARTIAL_CONTENT(206) 을 반환한다. Range 요청에 대한 응답 값이 Body 에 담겨져 있다. Spring boot - ResourceRegion HttpRange 는 Range Header 정보를 담든 객체다. Request Header 로부터 정보를 얻어올 수 있다. ResourceRegion 는 전달 받은 Resource 객체 로부터 Range 범위 만큼 나눠 가져오는 객체다. @RestController@Slf4jpublic class VideoController { @GetMapping(value = "/video") public ResponseEntity<ResourceRegion> streamVideo(@RequestHeader HttpHeaders headers) throws IOException { UrlResource video = new UrlResource("file:/Users/dongwoo-yang/spring-file/mysong.mp4"); ResourceRegion resourceRegion; final long size = 1000000L; long contentLength = video.contentLength(); Optional<HttpRange> optional = headers.getRange().stream().findFirst(); HttpRange httpRange; if (optional.isPresent()) { httpRange = optional.get(); long start = httpRange.getRangeStart(contentLength); long end = httpRange.getRangeEnd(contentLength); long rangeLength = Long.min(size, end - start + 1); resourceRegion = new ResourceRegion(video, start, rangeLength); } else { long rangeLength = Long.min(size, contentLength); resourceRegion = new ResourceRegion(video, 0, rangeLength); } return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT) .contentType(MediaTypeFactory.getMediaType(video).orElse(MediaType.APPLICATION_OCTET_STREAM)) .body(resourceRegion); }} 서버에서 전송해주는 Resource Size 가 1 MB(1000000) 로 잡혀 있어 1 MB 씩 부분적으로 응답받는 것을 확인할 수 있다.

0

Spring boot - MultipartFile 에서 발생하는 예외 처리

목차 Spring boot - StreamingResponseBody Spring boot - ResourceRegion Spring boot - 파일 다운로드 서비스 구현하기 Spring boot - 파일 업로드 서비스 구현하기 Spring boot - Resource 추상화 Spring boot - MultipartFile 에서 발생하는 예외 처리 Spring boot - MultipartFile 를 이용한 파일 업로드 Spring boot - Part 객체를 이용한 파일 업로드 MaxUploadSizeExceededException 예외MaxUploadSizeExceededException 예외가 발생하는 경우는 크게 두 가지가 있다. 첫 번째는 FileSizeLimitExceededException 예외가 발생했을 때 두 번째는 SizeLimitExceededException 예외가 발생했을 때다. FileSizeLimitExceededException 서버로 전송되는 각 파일 크기가 제한을 초과했을 때 발생하는 예외, 기본값은 1MB다.org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException: The field file exceeds its maximum permitted size of 1048576 bytes. SizeLimitExceededException 서버로 전송되는 모든 파일 크기가 제한을 초과했을 때 발생하는 예외, 기본값은 10MB다.org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException: the request was rejected because its size (29404104) exceeds the configured maximum (10485760)

0

Spring boot - Resource 추상화

목차 Spring boot - StreamingResponseBody Spring boot - ResourceRegion Spring boot - 파일 다운로드 서비스 구현하기 Spring boot - 파일 업로드 서비스 구현하기 Spring boot - Resource 추상화 Spring boot - MultipartFile 에서 발생하는 예외 처리 Spring boot - MultipartFile 를 이용한 파일 업로드 Spring boot - Part 객체를 이용한 파일 업로드 Spring boot Resource 추상화https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/resources.html Resource 는 java.net.URL 을 추상화 해 스프링 프레임워크에서 Low-Level에 있는 자원에 쉽게 접근할 수 있도록 지원한다 ClassPath를 기준으로 리소스를 가져오는 기능의 부재 ServletContext를 기준으로 상대 경로를 읽어오는 기능의 부재 Resource.java public interface Resource extends InputStreamSource { boolean exists(); URL getURL() throws IOException; URI getURI() throws IOException; File getFile() throws IOException; long contentLength() throws IOException; long lastModified() throws IOException; Resource createRelative(String relativePath) throws IOException; @Nullable String getFilename(); String getDescription(); default boolean isReadable() { return exists(); } default boolean isOpen() { return false; } default boolean isFile() { return false; } default ReadableByteChannel readableChannel() throws IOException { return Channels.newChannel(getInputStream()); }} Resource 구현체

0

Spring boot - 파일 다운로드 서비스 구현하기

목차 Spring boot - StreamingResponseBody Spring boot - ResourceRegion Spring boot - 파일 다운로드 서비스 구현하기 Spring boot - 파일 업로드 서비스 구현하기 Spring boot - Resource 추상화 Spring boot - MultipartFile 에서 발생하는 예외 처리 Spring boot - MultipartFile 를 이용한 파일 업로드 Spring boot - Part 객체를 이용한 파일 업로드 파일 다운로드서버에 저장된 파일을 다운 받기 위해서는 Response 해더에 Content-Disposition 정보가 필요합니다. String contentDisposition = "attachment; filename=\"" + encodedUploadFileName + "\""; Resource 객체로 업로드된 파일을 전송한다. UrlResource 객체를 이용해 특정 경로에 저장된 Resource 를 가져온 후 ResponseEntity 의 Body에 넣어서 반환한다. @GetMapping("/attach/{itemId}")public ResponseEntity<Resource> downloadAttach(@PathVariable Long itemId) throws MalformedURLException { Item item = itemRepository.findById(itemId); String storeFileName = item.getAttachFile().getStoreFileName(); String uploadFileName = item.getAttachFile().getUploadFileName(); UrlResource resource = new UrlResource("file:" + fileStore.getFullPath(storeFileName)); log.info("uploadFileName={}", uploadFileName); String encodedUploadFileName = UriUtils.encode(uploadFileName, StandardCharsets.UTF_8); String contentDisposition = "attachment; filename=\"" + encodedUploadFileName + "\""; return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition) .body(resource);} 전체 코드@Slf4j@Controller@RequiredArgsConstructorpublic class ItemController { private final ItemRepository itemRepository; private final FileStore fileStore; @GetMapping("/items/new") public String newItem(@ModelAttribute ItemForm form) { return "item-form"; } @PostMapping("/items/new") public String saveItem(@ModelAttribute ItemForm form, RedirectAttributes redirectAttributes) throws IOException { UploadFile attachFile = fileStore.storeFile(form.getAttachFile()); List<UploadFile> storeImageFiles = fileStore.storeFiles(form.getImageFiles()); //데이터베이스에 저장 Item item = new Item(); item.setItemName(form.getItemName()); item.setAttachFile(attachFile); item.setImageFiles(storeImageFiles); itemRepository.save(item); redirectAttributes.addAttribute("itemId", item.getId()); return "redirect:/items/{itemId}"; } @GetMapping("/items/{id}") public String items(@PathVariable Long id, Model model) { Item item = itemRepository.findById(id); model.addAttribute("item", item); return "item-view"; } @ResponseBody @GetMapping("/images/{filename}") public Resource downloadImage(@PathVariable String filename) throws MalformedURLException { return new UrlResource("file:" + fileStore.getFullPath(filename)); } @GetMapping("/attach/{itemId}") public ResponseEntity<Resource> downloadAttach(@PathVariable Long itemId) throws MalformedURLException { Item item = itemRepository.findById(itemId); String storeFileName = item.getAttachFile().getStoreFileName(); String uploadFileName = item.getAttachFile().getUploadFileName(); UrlResource resource = new UrlResource("file:" + fileStore.getFullPath(storeFileName)); log.info("uploadFileName={}", uploadFileName); String encodedUploadFileName = UriUtils.encode(uploadFileName, StandardCharsets.UTF_8); String contentDisposition = "attachment; filename=\"" + encodedUploadFileName + "\""; return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition) .body(resource); }}

0

Spring boot - 파일 업로드 서비스 구현하기

목차 Spring boot - StreamingResponseBody Spring boot - ResourceRegion Spring boot - 파일 다운로드 서비스 구현하기 Spring boot - 파일 업로드 서비스 구현하기 Spring boot - Resource 추상화 Spring boot - MultipartFile 에서 발생하는 예외 처리 Spring boot - MultipartFile 를 이용한 파일 업로드 Spring boot - Part 객체를 이용한 파일 업로드 DTO 생성하기@Datapublic class Item { private Long id; private String itemName; private UploadFile attachFile; // 여러개의 이미지 파일을 입력하기 위한 List private List<UploadFile> imageFiles;} 파일을 업로드 할때 중복된 이름을 가진 파일이 있을 경우 해당 파일로 업데이트 되는 문제가 발생합니다. 이를 해결하기 위해 파일을 저장할때는 다름 이름으로 저장하고 원래 파일이름을 별도로 저장합니다. 저장된 파일이름과 매핑되는 원래 파일 이름을 가져올 수 있도록 UploadFile 객체를 생성합니다. @Datapublic class UploadFile { private String uploadFileName; private String storeFileName; public UploadFile(String uploadFileName, String storeFileName) { this.uploadFileName = uploadFileName; this.storeFileName = storeFileName; }} 파일 저장 - 서비스 구현파일에서 확장자 추출하기

0

Spring boot - MultipartFile 를 이용한 파일 업로드

목차 Spring boot - StreamingResponseBody Spring boot - ResourceRegion Spring boot - 파일 다운로드 서비스 구현하기 Spring boot - 파일 업로드 서비스 구현하기 Spring boot - Resource 추상화 Spring boot - MultipartFile 에서 발생하는 예외 처리 Spring boot - MultipartFile 를 이용한 파일 업로드 Spring boot - Part 객체를 이용한 파일 업로드 MultipartFile 인터페이스 multipart/form-data 요청을 처리하기 위해 만들어진 객체 Spring 에서는 멀티파트 요청을 처리 하기 위해 MultipartFile 객체를 제공합니다. public interface MultipartFile extends InputStreamSource { // 전달 받은 Parameter Key값을 가져온다. String getName(); // 파일 명을 가져온다. @Nullable String getOriginalFilename(); // 파일의 ContentType을 가져온다. // pdf 파일의 경우 application/pdf, mp4 파일의 경우 video/mp4, png 파일의 경우 image/png @Nullable String getContentType(); // 전달 받은 Parameter Value값이 비어 있는지 확인한다. boolean isEmpty(); // 전달 받은 파일의 크기를 가져온다. // 파일은 byte 단위로 표시된다. long getSize(); byte[] getBytes() throws IOException; // 전달 받은 파일을 읽기 위한 InputStream을 가져온다. @Override InputStream getInputStream() throws IOException; // MultipartFile 객체를 이용해 Resource(MultipartFileResource) 객체를 반환 받는다. default Resource getResource() { return new MultipartFileResource(this); } // MultipartFile 내 데이터를 전달받은 File 객체에 저장한다. // 한번만 호출이 가능한 메소드다. void transferTo(File dest) throws IOException, IllegalStateException; // MultipartFile 내 데이터를 전달받은 Path 객체에 저장한다. default void transferTo(Path dest) throws IOException, IllegalStateException { FileCopyUtils.copy(getInputStream(), Files.newOutputStream(dest)); }} 파일 업로드 구현파일 업로드를 위한 Controller 구현 multipart/form-data 요청은 Form 형식으로 요청이 오기 때문에 RequestBody 어노테이션이 아니라 RequestParam 어노테이션을 이용해 데이터를 받는다.

0

Spring boot - 파일 업로드 2

목차 Spring boot - StreamingResponseBody Spring boot - ResourceRegion Spring boot - 파일 다운로드 서비스 구현하기 Spring boot - 파일 업로드 서비스 구현하기 Spring boot - Resource 추상화 Spring boot - MultipartFile 에서 발생하는 예외 처리 Spring boot - MultipartFile 를 이용한 파일 업로드 Spring boot - Part 객체를 이용한 파일 업로드 Spring boot 파일 업로드 2 getHeaderNames() part Type으로 전달된 데이터의 모든 해더 이름을 가져온다. getHeader(headerName) getHeader메소드에 헤더 이름을 넘겨주면 해더 이름이 갖는 요청의 모든 해더 정보를 가져온다. getSubmittedFileName() 파일 명을 가져오기 위한 메소드 getInputStream() Part에 저장된 데이터를 가져오기 위한 메소드 write(filepath) write 메소드에 파일이 저장될 File Path를 입력하면 해당 경로에 파일이 저장하는 메소드 Collection<Part> parts = request.getParts();log.info("parts={}", parts);for (Part part : parts) { log.info("==== PART ===="); log.info("name={}", part.getName()); Collection<String> headerNames = part.getHeaderNames(); for (String headerName : headerNames) { log.info("header {}: {}", headerName, part.getHeader(headerName)); } //편의 메서드 //content-disposition; filename log.info("submittedFilename={}", part.getSubmittedFileName()); log.info("size={}", part.getSize()); //part body size //데이터 읽기 InputStream inputStream = part.getInputStream(); String body = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); log.info("body={}", body); //파일에 저장하기 if (StringUtils.hasText(part.getSubmittedFileName())) { String fullPath = fileDir + part.getSubmittedFileName(); log.info("파일 저장 fullPath={}", fullPath); part.write(fullPath); }} @Slf4j@Controller@RequestMapping("/servlet/v2")public class ServletUploadControllerV2 { @Value("${file.dir}") private String fileDir; @GetMapping("/upload") public String newFile() { return "upload-form"; } @PostMapping("/upload") public String saveFileV1(HttpServletRequest request) throws ServletException, IOException { log.info("request={}", request); String itemName = request.getParameter("itemName"); log.info("itemName={}", itemName); Collection<Part> parts = request.getParts(); log.info("parts={}", parts); for (Part part : parts) { log.info("==== PART ===="); log.info("name={}", part.getName()); Collection<String> headerNames = part.getHeaderNames(); for (String headerName : headerNames) { log.info("header {}: {}", headerName, part.getHeader(headerName)); } //편의 메서드 //content-disposition; filename log.info("submittedFilename={}", part.getSubmittedFileName()); log.info("size={}", part.getSize()); //part body size //데이터 읽기 InputStream inputStream = part.getInputStream(); String body = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); log.info("body={}", body); //파일에 저장하기 if (StringUtils.hasText(part.getSubmittedFileName())) { String fullPath = fileDir + part.getSubmittedFileName(); log.info("파일 저장 fullPath={}", fullPath); part.write(fullPath); } } return "upload-form"; }}

0

Spring boot - Part 객체를 이용한 파일 업로드

목차 Spring boot - StreamingResponseBody Spring boot - ResourceRegion Spring boot - 파일 다운로드 서비스 구현하기 Spring boot - 파일 업로드 서비스 구현하기 Spring boot - Resource 추상화 Spring boot - MultipartFile 에서 발생하는 예외 처리 Spring boot - MultipartFile 를 이용한 파일 업로드 Spring boot - Part 객체를 이용한 파일 업로드 Spring boot 파일 업로드Form 태그에 enctype 옵션으로 multipart/form-data 를 명시하면 파일을 보낼 수 있다. <form th:action method="post" enctype="multipart/form-data"> <!DOCTYPE HTML><html xmlns:th="http://www.thymeleaf.org"><head> <meta charset="utf-8"></head><body><div class="container"> <div class="py-5 text-center"> <h2>상품 등록 폼</h2> </div> <h4 class="mb-3">상품 입력</h4> <form th:action method="post" enctype="multipart/form-data"> <ul> <li>상품명 <input type="text" name="itemName"></li> <li>파일<input type="file" name="file" ></li> </ul> <input type="submit"/> </form></div> <!-- /container --></body></html> Part 인터페이스Servlet 에서는 HTTP 멀티파트 요청을 처리하기 위해 Part 인터페이스를 제공합니다. getHeaderNames() part Type으로 전달된 데이터의 모든 해더 이름을 가져온다. getContentType() 파일의 ContentType을 가져온다. getHeader(headerName) getHeader메소드에 헤더 이름을 넘겨주면 해더 이름이 갖는 요청의 모든 해더 정보를 가져온다. getSubmittedFileName() 파일 명을 가져오기 위한 메소드 getInputStream() Part에 저장된 데이터를 가져오기 위한 메소드 write(filepath) write 메소드에 파일이 저장될 File Path를 입력하면 해당 경로에 파일이 저장하는 메소드