package kr.itn.itnhub.shared; import kr.itn.itnhub.config.CurrentUser; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; 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.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.security.Principal; import java.util.List; import java.util.Map; /** 자료실. 기관에 딸리지 않는 공용 서식을 올려두고 내려받는다(요구사항 [26]). */ @RestController public class SharedFileController { private final SharedFileService service; private final CurrentUser currentUser; public SharedFileController(SharedFileService service, CurrentUser currentUser) { this.service = service; this.currentUser = currentUser; } @GetMapping("/api/shared-files") public List list() { return service.list(); } @PostMapping("/api/shared-files") public List add(@RequestPart("file") MultipartFile file, @RequestPart(value = "description", required = false) String description, Principal principal) throws IOException { String name = file.getOriginalFilename() == null ? "file" : file.getOriginalFilename(); return service.add(name, description, file.getContentType(), file.getBytes(), currentUser.displayName(principal)); } @PutMapping("/api/shared-files/{id}") public List updateDescription(@PathVariable Long id, @RequestBody Map body) { return service.updateDescription(id, body.get("description")); } /** 한글 파일명이 깨지지 않게 보고서 다운로드와 같은 RFC 5987 인코딩을 쓴다. */ @GetMapping("/api/shared-files/{id}") public ResponseEntity download(@PathVariable Long id) { SharedFileMapper.Content found = service.content(id); String encodedName = URLEncoder.encode(found.fileName(), StandardCharsets.UTF_8) .replace("+", "%20"); MediaType type = found.contentType() == null || found.contentType().isBlank() ? MediaType.APPLICATION_OCTET_STREAM : MediaType.parseMediaType(found.contentType()); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=UTF-8''" + encodedName) .contentType(type) .body(found.content()); } @ResponseStatus(HttpStatus.NO_CONTENT) @DeleteMapping("/api/shared-files/{id}") public void delete(@PathVariable Long id) { service.delete(id); } @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(IllegalArgumentException.class) public Map handleBadRequest(IllegalArgumentException e) { return Map.of("message", e.getMessage()); } }