Merge pull request '대용량 다운로드 수정' (#68) from feat/infer_dev_260211 into develop
Reviewed-on: #68
This commit was merged in pull request #68.
This commit is contained in:
@@ -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<StreamingResponseBody> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
) {}
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
package com.kamco.cd.kamcoback.inference;
|
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.common.exception.CustomApiException;
|
||||||
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
|
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
|
||||||
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto;
|
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 io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -35,7 +34,9 @@ import java.util.Map;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
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.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
|
|
||||||
|
|
||||||
@Tag(name = "추론관리", description = "추론관리 API")
|
@Tag(name = "추론관리", description = "추론관리 API")
|
||||||
@Log4j2
|
@Log4j2
|
||||||
@@ -58,7 +58,6 @@ public class InferenceResultApiController {
|
|||||||
private final InferenceResultService inferenceResultService;
|
private final InferenceResultService inferenceResultService;
|
||||||
private final MapSheetMngService mapSheetMngService;
|
private final MapSheetMngService mapSheetMngService;
|
||||||
private final ModelMngService modelMngService;
|
private final ModelMngService modelMngService;
|
||||||
private final DownloadExecutor downloadExecutor;
|
|
||||||
|
|
||||||
@Operation(summary = "추론관리 목록", description = "어드민 홈 > 추론관리 > 추론관리 > 추론관리 목록")
|
@Operation(summary = "추론관리 목록", description = "어드민 홈 > 추론관리 > 추론관리 > 추론관리 목록")
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
@@ -359,7 +358,7 @@ public class InferenceResultApiController {
|
|||||||
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
|
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
|
||||||
})
|
})
|
||||||
@GetMapping(value = "/download/{uuid}")
|
@GetMapping(value = "/download/{uuid}")
|
||||||
public ResponseEntity<StreamingResponseBody> download(
|
public ResponseEntity<Resource> download(
|
||||||
@Parameter(example = "69c4e56c-e0bf-4742-9225-bba9aae39052") @PathVariable UUID uuid)
|
@Parameter(example = "69c4e56c-e0bf-4742-9225-bba9aae39052") @PathVariable UUID uuid)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
@@ -375,10 +374,18 @@ public class InferenceResultApiController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Path zipPath = Path.of(path);
|
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);
|
log.info("shp download request path = {}", path);
|
||||||
|
|
||||||
return downloadExecutor.stream(
|
return ResponseEntity.ok()
|
||||||
new DownloadSpec(uuid, zipPath, uid + ".zip", MediaType.APPLICATION_OCTET_STREAM));
|
.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 파일 다운로드 이력 조회")
|
@Operation(summary = "shp 파일 다운로드 이력 조회", description = "추론관리 분석결과 shp 파일 다운로드 이력 조회")
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.kamco.cd.kamcoback.label;
|
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.config.api.ApiResponseDto;
|
||||||
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto;
|
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto;
|
||||||
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.InferenceDetail;
|
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto.InferenceDetail;
|
||||||
@@ -31,6 +30,7 @@ import java.util.List;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.coyote.BadRequestException;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
@@ -53,7 +53,6 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
public class LabelAllocateApiController {
|
public class LabelAllocateApiController {
|
||||||
|
|
||||||
private final LabelAllocateService labelAllocateService;
|
private final LabelAllocateService labelAllocateService;
|
||||||
private final DownloadExecutor downloadExecutor;
|
|
||||||
|
|
||||||
@Value("${file.dataset-response}")
|
@Value("${file.dataset-response}")
|
||||||
private String responsePath;
|
private String responsePath;
|
||||||
@@ -386,25 +385,22 @@ public class LabelAllocateApiController {
|
|||||||
@Parameter(example = "6d8d49dc-0c9d-4124-adc7-b9ca610cc394") @PathVariable UUID uuid)
|
@Parameter(example = "6d8d49dc-0c9d-4124-adc7-b9ca610cc394") @PathVariable UUID uuid)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
// if (!labelAllocateService.isDownloadable(uuid)) {
|
if (!labelAllocateService.isDownloadable(uuid)) {
|
||||||
// throw new BadRequestException();
|
throw new BadRequestException();
|
||||||
// }
|
}
|
||||||
|
|
||||||
String uid = labelAllocateService.findLearnUid(uuid);
|
String uid = labelAllocateService.findLearnUid(uuid);
|
||||||
Path zipPath = Paths.get(responsePath).resolve(uid + ".zip");
|
Path zipPath = Paths.get(responsePath).resolve(uid + ".zip");
|
||||||
long size = Files.size(zipPath);
|
long size = Files.size(zipPath);
|
||||||
Resource resource = new org.springframework.core.io.UrlResource(zipPath.toUri());
|
Resource resource = new org.springframework.core.io.UrlResource(zipPath.toUri());
|
||||||
|
|
||||||
return ResponseEntity.ok()
|
return ResponseEntity.ok()
|
||||||
.header(
|
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + uid + ".zip" + "\"")
|
||||||
HttpHeaders.CONTENT_DISPOSITION,
|
|
||||||
"attachment; filename=\"" + zipPath.getFileName().toString() + "\"")
|
|
||||||
.header(HttpHeaders.ACCEPT_RANGES, "bytes")
|
.header(HttpHeaders.ACCEPT_RANGES, "bytes")
|
||||||
.header("X-Accel-Buffering", "no") // nginx/ingress 버퍼링 방지 힌트
|
.header("X-Accel-Buffering", "no") // nginx/ingress 버퍼링 방지 힌트
|
||||||
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
.contentLength(size)
|
.contentLength(size)
|
||||||
.body(resource);
|
.body(resource);
|
||||||
// return downloadExecutor.stream(
|
|
||||||
// new DownloadSpec(uuid, zipPath, uid + ".zip", MediaType.APPLICATION_OCTET_STREAM));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "라벨 파일 다운로드 이력 조회", description = "라벨 파일 다운로드 이력 조회")
|
@Operation(summary = "라벨 파일 다운로드 이력 조회", description = "라벨 파일 다운로드 이력 조회")
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@Transactional
|
@Transactional(readOnly = true)
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class LabelAllocateService {
|
public class LabelAllocateService {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user