diff --git a/src/main/java/com/kamco/cd/kamcoback/common/download/DownloadExecutor.java b/src/main/java/com/kamco/cd/kamcoback/common/download/DownloadExecutor.java deleted file mode 100644 index edb80175..00000000 --- a/src/main/java/com/kamco/cd/kamcoback/common/download/DownloadExecutor.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.kamco.cd.kamcoback.common.download; - -import com.kamco.cd.kamcoback.common.download.dto.DownloadSpec; -import com.kamco.cd.kamcoback.common.utils.UserUtil; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; -import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; - -@Service -@RequiredArgsConstructor -public class DownloadExecutor { - - private final UserUtil userUtil; - - public ResponseEntity stream(DownloadSpec spec) throws IOException { - - if (!Files.isReadable(spec.filePath())) { - return ResponseEntity.notFound().build(); - } - - StreamingResponseBody body = - os -> { - try (InputStream in = Files.newInputStream(spec.filePath())) { - in.transferTo(os); - os.flush(); - } catch (Exception e) { - // 고용량은 중간 끊김 흔하니까 throw 금지 - } - }; - - String fileName = - spec.downloadName() != null - ? spec.downloadName() - : spec.filePath().getFileName().toString(); - - return ResponseEntity.ok() - .contentType( - spec.contentType() != null ? spec.contentType() : MediaType.APPLICATION_OCTET_STREAM) - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"") - .body(body); - } -} diff --git a/src/main/java/com/kamco/cd/kamcoback/common/download/dto/DownloadSpec.java b/src/main/java/com/kamco/cd/kamcoback/common/download/dto/DownloadSpec.java deleted file mode 100644 index bc3b5570..00000000 --- a/src/main/java/com/kamco/cd/kamcoback/common/download/dto/DownloadSpec.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.kamco.cd.kamcoback.common.download.dto; - -import java.nio.file.Path; -import java.util.UUID; -import org.springframework.http.MediaType; - -public record DownloadSpec( - UUID uuid, // 다운로드 식별(로그/정책용) - Path filePath, // 실제 파일 경로 - String downloadName, // 사용자에게 보일 파일명 - MediaType contentType // 보통 OCTET_STREAM - ) {} diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/InferenceResultApiController.java b/src/main/java/com/kamco/cd/kamcoback/inference/InferenceResultApiController.java index f1c09000..30fa5080 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/InferenceResultApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/InferenceResultApiController.java @@ -1,7 +1,5 @@ package com.kamco.cd.kamcoback.inference; -import com.kamco.cd.kamcoback.common.download.DownloadExecutor; -import com.kamco.cd.kamcoback.common.download.dto.DownloadSpec; import com.kamco.cd.kamcoback.common.exception.CustomApiException; import com.kamco.cd.kamcoback.config.api.ApiResponseDto; import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto; @@ -28,6 +26,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.time.LocalDate; import java.util.List; @@ -35,7 +34,9 @@ import java.util.Map; import java.util.UUID; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; +import org.springframework.core.io.Resource; import org.springframework.data.domain.Page; +import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; @@ -46,7 +47,6 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; @Tag(name = "추론관리", description = "추론관리 API") @Log4j2 @@ -58,7 +58,6 @@ public class InferenceResultApiController { private final InferenceResultService inferenceResultService; private final MapSheetMngService mapSheetMngService; private final ModelMngService modelMngService; - private final DownloadExecutor downloadExecutor; @Operation(summary = "추론관리 목록", description = "어드민 홈 > 추론관리 > 추론관리 > 추론관리 목록") @ApiResponses( @@ -359,7 +358,7 @@ public class InferenceResultApiController { @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping(value = "/download/{uuid}") - public ResponseEntity download( + public ResponseEntity download( @Parameter(example = "69c4e56c-e0bf-4742-9225-bba9aae39052") @PathVariable UUID uuid) throws IOException { @@ -375,10 +374,18 @@ public class InferenceResultApiController { } Path zipPath = Path.of(path); + long size = Files.size(zipPath); + Resource resource = new org.springframework.core.io.UrlResource(zipPath.toUri()); + log.info("shp download request path = {}", path); - return downloadExecutor.stream( - new DownloadSpec(uuid, zipPath, uid + ".zip", MediaType.APPLICATION_OCTET_STREAM)); + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + uid + ".zip" + "\"") + .header(HttpHeaders.ACCEPT_RANGES, "bytes") + .header("X-Accel-Buffering", "no") // nginx/ingress 버퍼링 방지 힌트 + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .contentLength(size) + .body(resource); } @Operation(summary = "shp 파일 다운로드 이력 조회", description = "추론관리 분석결과 shp 파일 다운로드 이력 조회") diff --git a/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java b/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java index 1dc82963..fdbe6e2d 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/LabelAllocateApiController.java @@ -1,6 +1,5 @@ package com.kamco.cd.kamcoback.label; -import com.kamco.cd.kamcoback.common.download.DownloadExecutor; import com.kamco.cd.kamcoback.config.api.ApiResponseDto; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto; import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.InferenceDetail; @@ -31,6 +30,7 @@ import java.util.List; import java.util.UUID; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.coyote.BadRequestException; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.Resource; import org.springframework.data.domain.Page; @@ -53,7 +53,6 @@ import org.springframework.web.bind.annotation.RestController; public class LabelAllocateApiController { private final LabelAllocateService labelAllocateService; - private final DownloadExecutor downloadExecutor; @Value("${file.dataset-response}") private String responsePath; @@ -386,25 +385,22 @@ public class LabelAllocateApiController { @Parameter(example = "6d8d49dc-0c9d-4124-adc7-b9ca610cc394") @PathVariable UUID uuid) throws IOException { - // if (!labelAllocateService.isDownloadable(uuid)) { - // throw new BadRequestException(); - // } + if (!labelAllocateService.isDownloadable(uuid)) { + throw new BadRequestException(); + } String uid = labelAllocateService.findLearnUid(uuid); Path zipPath = Paths.get(responsePath).resolve(uid + ".zip"); long size = Files.size(zipPath); Resource resource = new org.springframework.core.io.UrlResource(zipPath.toUri()); + return ResponseEntity.ok() - .header( - HttpHeaders.CONTENT_DISPOSITION, - "attachment; filename=\"" + zipPath.getFileName().toString() + "\"") + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + uid + ".zip" + "\"") .header(HttpHeaders.ACCEPT_RANGES, "bytes") .header("X-Accel-Buffering", "no") // nginx/ingress 버퍼링 방지 힌트 .contentType(MediaType.APPLICATION_OCTET_STREAM) .contentLength(size) .body(resource); - // return downloadExecutor.stream( - // new DownloadSpec(uuid, zipPath, uid + ".zip", MediaType.APPLICATION_OCTET_STREAM)); } @Operation(summary = "라벨 파일 다운로드 이력 조회", description = "라벨 파일 다운로드 이력 조회") diff --git a/src/main/java/com/kamco/cd/kamcoback/label/service/LabelAllocateService.java b/src/main/java/com/kamco/cd/kamcoback/label/service/LabelAllocateService.java index 25f9d5ad..72892a6a 100644 --- a/src/main/java/com/kamco/cd/kamcoback/label/service/LabelAllocateService.java +++ b/src/main/java/com/kamco/cd/kamcoback/label/service/LabelAllocateService.java @@ -31,7 +31,7 @@ import org.springframework.transaction.annotation.Transactional; @Slf4j @Service -@Transactional +@Transactional(readOnly = true) @RequiredArgsConstructor public class LabelAllocateService {