Compare commits
27 Commits
feat/infer
...
3f6737706a
| Author | SHA1 | Date | |
|---|---|---|---|
| 3f6737706a | |||
| 0df7d7c5cf | |||
| 3724528ea9 | |||
| 9885c19b50 | |||
| 079a899822 | |||
| 5b09b2e29a | |||
| 58a73de9ab | |||
| 4cbd2b8d76 | |||
| f4a890bec8 | |||
| 89504e4156 | |||
| 783609b015 | |||
| 5d33190c31 | |||
| 92232e13f1 | |||
| 81b0b55d57 | |||
| 83ef7e36ed | |||
| 0d13e6989f | |||
| 4342df9bf5 | |||
| 8f9585b516 | |||
| 43b5a79031 | |||
| 3ba3b05f2f | |||
| fffc2efd96 | |||
| 82e3250fd4 | |||
| 470f2191b7 | |||
| c127531412 | |||
| 61cfd8240a | |||
| 54b6712273 | |||
| b2141e98c0 |
@@ -1,6 +1,5 @@
|
|||||||
# 1단계에서 만든 로컬 베이스 이미지를 사용
|
# 1단계에서 만든 로컬 베이스 이미지를 사용
|
||||||
FROM 192.168.2.73:18082/kamco-cd/base-java21-gdal:1.0
|
FROM 127.0.0.1:18082/kamco-cd/base-java21-gdal:1.0
|
||||||
FROM 192.168.2.73:18082/kamco-cd/base-java21-gdal:1.0
|
|
||||||
|
|
||||||
# 사용자 설정 (앱 별로 다를 수 있으므로 여기에 유지)
|
# 사용자 설정 (앱 별로 다를 수 있으므로 여기에 유지)
|
||||||
ARG UID=1000
|
ARG UID=1000
|
||||||
|
|||||||
@@ -15,10 +15,6 @@ services:
|
|||||||
- SPRING_PROFILES_ACTIVE=dev
|
- SPRING_PROFILES_ACTIVE=dev
|
||||||
- TZ=Asia/Seoul
|
- TZ=Asia/Seoul
|
||||||
volumes:
|
volumes:
|
||||||
- /mnt/nfs_share/images:/app/original-images
|
|
||||||
- /mnt/nfs_share/model_output:/app/model-outputs
|
|
||||||
- /mnt/nfs_share/train_dataset:/app/train-dataset
|
|
||||||
- /mnt/nfs_share/tmp:/app/tmp
|
|
||||||
- /kamco-nfs:/kamco-nfs
|
- /kamco-nfs:/kamco-nfs
|
||||||
networks:
|
networks:
|
||||||
- kamco-cds
|
- kamco-cds
|
||||||
|
|||||||
@@ -15,11 +15,7 @@ services:
|
|||||||
- SPRING_PROFILES_ACTIVE=dev
|
- SPRING_PROFILES_ACTIVE=dev
|
||||||
- TZ=Asia/Seoul
|
- TZ=Asia/Seoul
|
||||||
volumes:
|
volumes:
|
||||||
- /mnt/nfs_share/images:/app/original-images
|
- /data:/kamco-nfs
|
||||||
- /mnt/nfs_share/model_output:/app/model-outputs
|
|
||||||
- /mnt/nfs_share/train_dataset:/app/train-dataset
|
|
||||||
- /mnt/nfs_share/tmp:/app/tmp
|
|
||||||
- /kamco-nfs:/kamco-nfs
|
|
||||||
networks:
|
networks:
|
||||||
- kamco-cds
|
- kamco-cds
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|||||||
@@ -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
|
|
||||||
) {}
|
|
||||||
@@ -7,11 +7,14 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@Log4j2
|
@Log4j2
|
||||||
@Component
|
@Component
|
||||||
public class ExternalJarRunner {
|
public class ExternalJarRunner {
|
||||||
|
@Value("${spring.profiles.active}")
|
||||||
|
private String profile;
|
||||||
|
|
||||||
private static final long TIMEOUT_MINUTES = TimeUnit.DAYS.toMinutes(3);
|
private static final long TIMEOUT_MINUTES = TimeUnit.DAYS.toMinutes(3);
|
||||||
|
|
||||||
@@ -40,7 +43,7 @@ public class ExternalJarRunner {
|
|||||||
if (mode != null && !mode.isEmpty()) {
|
if (mode != null && !mode.isEmpty()) {
|
||||||
addArg(args, "converter.mode", mode);
|
addArg(args, "converter.mode", mode);
|
||||||
}
|
}
|
||||||
|
addArg(args, "spring.profiles.active", profile);
|
||||||
execJar(jarPath, args);
|
execJar(jarPath, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +60,7 @@ public class ExternalJarRunner {
|
|||||||
addArg(args, "upload-shp", register);
|
addArg(args, "upload-shp", register);
|
||||||
// addArg(args, "layer", layer);
|
// addArg(args, "layer", layer);
|
||||||
|
|
||||||
|
addArg(args, "spring.profiles.active", profile);
|
||||||
execJar(jarPath, args);
|
execJar(jarPath, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ public class SecurityConfig {
|
|||||||
"/api/members/*/password",
|
"/api/members/*/password",
|
||||||
"/v3/api-docs/**",
|
"/v3/api-docs/**",
|
||||||
"/chunk_upload_test.html",
|
"/chunk_upload_test.html",
|
||||||
|
"/download_progress_test.html",
|
||||||
"/api/model/file-chunk-upload",
|
"/api/model/file-chunk-upload",
|
||||||
"/api/upload/file-chunk-upload",
|
"/api/upload/file-chunk-upload",
|
||||||
"/api/upload/chunk-upload-complete",
|
"/api/upload/chunk-upload-complete",
|
||||||
|
|||||||
@@ -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,13 +26,17 @@ 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;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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;
|
||||||
@@ -45,9 +47,9 @@ 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
|
||||||
@RequestMapping("/api/inference")
|
@RequestMapping("/api/inference")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@RestController
|
@RestController
|
||||||
@@ -56,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(
|
||||||
@@ -357,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 {
|
||||||
|
|
||||||
@@ -373,9 +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());
|
||||||
|
|
||||||
return downloadExecutor.stream(
|
log.info("shp download request path = {}", path);
|
||||||
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 파일 다운로드 이력 조회")
|
@Operation(summary = "shp 파일 다운로드 이력 조회", description = "추론관리 분석결과 shp 파일 다운로드 이력 조회")
|
||||||
|
|||||||
@@ -1,7 +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.common.download.dto.DownloadSpec;
|
|
||||||
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;
|
||||||
@@ -25,15 +23,17 @@ 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.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.List;
|
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.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.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
@@ -43,7 +43,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;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Tag(name = "라벨링 작업 관리", description = "라벨링 작업 배정 및 통계 조회 API")
|
@Tag(name = "라벨링 작업 관리", description = "라벨링 작업 배정 및 통계 조회 API")
|
||||||
@@ -53,7 +52,6 @@ import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBo
|
|||||||
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;
|
||||||
@@ -382,19 +380,25 @@ public class LabelAllocateApiController {
|
|||||||
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
|
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
|
||||||
})
|
})
|
||||||
@GetMapping("/download/{uuid}")
|
@GetMapping("/download/{uuid}")
|
||||||
public ResponseEntity<StreamingResponseBody> download(
|
public ResponseEntity<Resource> download(
|
||||||
@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);
|
||||||
|
Resource resource = new org.springframework.core.io.UrlResource(zipPath.toUri());
|
||||||
|
|
||||||
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 = "라벨 파일 다운로드 이력 조회", 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 {
|
||||||
|
|
||||||
|
|||||||
@@ -134,7 +134,6 @@ public class MapSheetMngService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MngDto mngDto = mapSheetMngCoreService.findMapSheetMng(errDto.getMngYyyy());
|
MngDto mngDto = mapSheetMngCoreService.findMapSheetMng(errDto.getMngYyyy());
|
||||||
String targetYearDir = mngDto.getMngPath();
|
|
||||||
|
|
||||||
// 중복체크 -> 도엽50k/uuid 경로에 업로드 할 거라 overwrite 되지 않음
|
// 중복체크 -> 도엽50k/uuid 경로에 업로드 할 거라 overwrite 되지 않음
|
||||||
// if (!overwrite) {
|
// if (!overwrite) {
|
||||||
|
|||||||
@@ -41,21 +41,6 @@ public class ModelMngApiController {
|
|||||||
|
|
||||||
private final ModelMngService modelMngService;
|
private final ModelMngService modelMngService;
|
||||||
|
|
||||||
@Value("${file.sync-root-dir}")
|
|
||||||
private String syncRootDir;
|
|
||||||
|
|
||||||
@Value("${file.sync-tmp-dir}")
|
|
||||||
private String syncTmpDir;
|
|
||||||
|
|
||||||
@Value("${file.sync-file-extention}")
|
|
||||||
private String syncFileExtention;
|
|
||||||
|
|
||||||
@Value("${file.dataset-dir}")
|
|
||||||
private String datasetDir;
|
|
||||||
|
|
||||||
@Value("${file.dataset-tmp-dir}")
|
|
||||||
private String datasetTmpDir;
|
|
||||||
|
|
||||||
@Value("${file.model-dir}")
|
@Value("${file.model-dir}")
|
||||||
private String modelDir;
|
private String modelDir;
|
||||||
|
|
||||||
|
|||||||
@@ -35,27 +35,6 @@ public class ModelMngService {
|
|||||||
|
|
||||||
private final UploadService uploadService;
|
private final UploadService uploadService;
|
||||||
|
|
||||||
@Value("${file.sync-root-dir}")
|
|
||||||
private String syncRootDir;
|
|
||||||
|
|
||||||
@Value("${file.sync-tmp-dir}")
|
|
||||||
private String syncTmpDir;
|
|
||||||
|
|
||||||
@Value("${file.sync-file-extention}")
|
|
||||||
private String syncFileExtention;
|
|
||||||
|
|
||||||
@Value("${file.dataset-dir}")
|
|
||||||
private String datasetDir;
|
|
||||||
|
|
||||||
@Value("${file.dataset-tmp-dir}")
|
|
||||||
private String datasetTmpDir;
|
|
||||||
|
|
||||||
@Value("${file.model-dir}")
|
|
||||||
private String modelDir;
|
|
||||||
|
|
||||||
@Value("${file.model-tmp-dir}")
|
|
||||||
private String modelTmpDir;
|
|
||||||
|
|
||||||
@Value("${file.pt-path}")
|
@Value("${file.pt-path}")
|
||||||
private String ptPath;
|
private String ptPath;
|
||||||
|
|
||||||
|
|||||||
@@ -257,16 +257,16 @@ public class LabelAllocateCoreService {
|
|||||||
|
|
||||||
// 파일이 있는지만 확인
|
// 파일이 있는지만 확인
|
||||||
Path path = Paths.get(responsePath).resolve(dto.getLearnUid() + ".zip");
|
Path path = Paths.get(responsePath).resolve(dto.getLearnUid() + ".zip");
|
||||||
|
if (!Files.isRegularFile(path)) return false; // exists 포함
|
||||||
|
|
||||||
if (!Files.exists(path) || !Files.isRegularFile(path)) {
|
String state = dto.getAnalState();
|
||||||
// 실제 파일만 true (디렉터리는 제외)
|
boolean isLabelingIng =
|
||||||
return false;
|
LabelMngState.ASSIGNED.getId().equals(state) || LabelMngState.ING.getId().equals(state);
|
||||||
}
|
|
||||||
|
|
||||||
// 다운로드 확인할 학습데이터가 라벨링중인 경우 파일 생성여부가 정상인지 확인
|
if (isLabelingIng) {
|
||||||
if (dto.getAnalState().equals(LabelMngState.ASSIGNED.getId())
|
Long analId = dto.getAnalId();
|
||||||
|| dto.getAnalState().equals(LabelMngState.ING.getId())) {
|
if (analId == null) return false;
|
||||||
return batchStepHistoryRepository.isDownloadable(dto.getAnalId());
|
return batchStepHistoryRepository.isDownloadable(analId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -306,10 +306,6 @@ public class TrainingDataLabelRepositoryImpl extends QuerydslRepositorySupport
|
|||||||
queryFactory
|
queryFactory
|
||||||
.select(labelingAssignmentEntity.count())
|
.select(labelingAssignmentEntity.count())
|
||||||
.from(labelingAssignmentEntity)
|
.from(labelingAssignmentEntity)
|
||||||
.innerJoin(mapSheetAnalInferenceEntity)
|
|
||||||
.on(
|
|
||||||
labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id),
|
|
||||||
mapSheetAnalInferenceEntity.analState.ne(LabelMngState.FINISH.getId()))
|
|
||||||
.where(labelingAssignmentEntity.workerUid.eq(userId))
|
.where(labelingAssignmentEntity.workerUid.eq(userId))
|
||||||
.fetchOne();
|
.fetchOne();
|
||||||
|
|
||||||
@@ -330,10 +326,6 @@ public class TrainingDataLabelRepositoryImpl extends QuerydslRepositorySupport
|
|||||||
queryFactory
|
queryFactory
|
||||||
.select(labelingAssignmentEntity.count())
|
.select(labelingAssignmentEntity.count())
|
||||||
.from(labelingAssignmentEntity)
|
.from(labelingAssignmentEntity)
|
||||||
.innerJoin(mapSheetAnalInferenceEntity)
|
|
||||||
.on(
|
|
||||||
labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id),
|
|
||||||
mapSheetAnalInferenceEntity.analState.ne(LabelMngState.FINISH.getId()))
|
|
||||||
.where(
|
.where(
|
||||||
labelingAssignmentEntity.workerUid.eq(userId),
|
labelingAssignmentEntity.workerUid.eq(userId),
|
||||||
labelingAssignmentEntity.workState.eq("ASSIGNED"))
|
labelingAssignmentEntity.workState.eq("ASSIGNED"))
|
||||||
@@ -362,10 +354,6 @@ public class TrainingDataLabelRepositoryImpl extends QuerydslRepositorySupport
|
|||||||
queryFactory
|
queryFactory
|
||||||
.select(labelingAssignmentEntity.count())
|
.select(labelingAssignmentEntity.count())
|
||||||
.from(labelingAssignmentEntity)
|
.from(labelingAssignmentEntity)
|
||||||
.innerJoin(mapSheetAnalInferenceEntity)
|
|
||||||
.on(
|
|
||||||
labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id),
|
|
||||||
mapSheetAnalInferenceEntity.analState.ne(LabelMngState.FINISH.getId()))
|
|
||||||
.where(
|
.where(
|
||||||
labelingAssignmentEntity.workerUid.eq(userId),
|
labelingAssignmentEntity.workerUid.eq(userId),
|
||||||
labelingAssignmentEntity.workState.in(
|
labelingAssignmentEntity.workState.in(
|
||||||
|
|||||||
@@ -314,10 +314,6 @@ public class TrainingDataReviewRepositoryImpl extends QuerydslRepositorySupport
|
|||||||
queryFactory
|
queryFactory
|
||||||
.select(labelingAssignmentEntity.count())
|
.select(labelingAssignmentEntity.count())
|
||||||
.from(labelingAssignmentEntity)
|
.from(labelingAssignmentEntity)
|
||||||
.innerJoin(mapSheetAnalInferenceEntity)
|
|
||||||
.on(
|
|
||||||
labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id),
|
|
||||||
mapSheetAnalInferenceEntity.analState.ne(LabelMngState.FINISH.getId()))
|
|
||||||
.where(labelingAssignmentEntity.inspectorUid.eq(userId))
|
.where(labelingAssignmentEntity.inspectorUid.eq(userId))
|
||||||
.fetchOne();
|
.fetchOne();
|
||||||
|
|
||||||
@@ -338,10 +334,6 @@ public class TrainingDataReviewRepositoryImpl extends QuerydslRepositorySupport
|
|||||||
queryFactory
|
queryFactory
|
||||||
.select(labelingAssignmentEntity.count())
|
.select(labelingAssignmentEntity.count())
|
||||||
.from(labelingAssignmentEntity)
|
.from(labelingAssignmentEntity)
|
||||||
.innerJoin(mapSheetAnalInferenceEntity)
|
|
||||||
.on(
|
|
||||||
labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id),
|
|
||||||
mapSheetAnalInferenceEntity.analState.ne(LabelMngState.FINISH.getId()))
|
|
||||||
.where(
|
.where(
|
||||||
labelingAssignmentEntity.inspectorUid.eq(userId),
|
labelingAssignmentEntity.inspectorUid.eq(userId),
|
||||||
labelingAssignmentEntity.inspectState.eq("UNCONFIRM"))
|
labelingAssignmentEntity.inspectState.eq("UNCONFIRM"))
|
||||||
@@ -370,10 +362,6 @@ public class TrainingDataReviewRepositoryImpl extends QuerydslRepositorySupport
|
|||||||
queryFactory
|
queryFactory
|
||||||
.select(labelingAssignmentEntity.count())
|
.select(labelingAssignmentEntity.count())
|
||||||
.from(labelingAssignmentEntity)
|
.from(labelingAssignmentEntity)
|
||||||
.innerJoin(mapSheetAnalInferenceEntity)
|
|
||||||
.on(
|
|
||||||
labelingAssignmentEntity.analUid.eq(mapSheetAnalInferenceEntity.id),
|
|
||||||
mapSheetAnalInferenceEntity.analState.ne(LabelMngState.FINISH.getId()))
|
|
||||||
.where(
|
.where(
|
||||||
labelingAssignmentEntity.inspectorUid.eq(userId),
|
labelingAssignmentEntity.inspectorUid.eq(userId),
|
||||||
labelingAssignmentEntity.inspectState.in("COMPLETE", "EXCEPT"),
|
labelingAssignmentEntity.inspectState.in("COMPLETE", "EXCEPT"),
|
||||||
|
|||||||
@@ -29,27 +29,12 @@ public class UploadApiController {
|
|||||||
|
|
||||||
private final UploadService uploadService;
|
private final UploadService uploadService;
|
||||||
|
|
||||||
@Value("${file.sync-root-dir}")
|
|
||||||
private String syncRootDir;
|
|
||||||
|
|
||||||
@Value("${file.sync-tmp-dir}")
|
|
||||||
private String syncTmpDir;
|
|
||||||
|
|
||||||
@Value("${file.sync-file-extention}")
|
|
||||||
private String syncFileExtention;
|
|
||||||
|
|
||||||
@Value("${file.dataset-dir}")
|
@Value("${file.dataset-dir}")
|
||||||
private String datasetDir;
|
private String datasetDir;
|
||||||
|
|
||||||
@Value("${file.dataset-tmp-dir}")
|
@Value("${file.dataset-tmp-dir}")
|
||||||
private String datasetTmpDir;
|
private String datasetTmpDir;
|
||||||
|
|
||||||
@Value("${file.model-dir}")
|
|
||||||
private String modelDir;
|
|
||||||
|
|
||||||
@Value("${file.model-tmp-dir}")
|
|
||||||
private String modelTmpDir;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@Operation(summary = "데이터셋 대용량 업로드 세션 시작", description = "데이터셋 대용량 파일 업로드 세션을 시작합니다.")
|
@Operation(summary = "데이터셋 대용량 업로드 세션 시작", description = "데이터셋 대용량 파일 업로드 세션을 시작합니다.")
|
||||||
@ApiResponses(
|
@ApiResponses(
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ spring:
|
|||||||
format_sql: true # ⚠️ 선택 - SQL 포맷팅 (가독성)
|
format_sql: true # ⚠️ 선택 - SQL 포맷팅 (가독성)
|
||||||
jdbc:
|
jdbc:
|
||||||
batch_size: 1000 # ✅ 추가 (JDBC batch)
|
batch_size: 1000 # ✅ 추가 (JDBC batch)
|
||||||
|
mvc:
|
||||||
|
async:
|
||||||
|
request-timeout: 300s # 5분 (예: 30s, 120s, 10m 등도 가능)
|
||||||
|
|
||||||
datasource:
|
datasource:
|
||||||
url: jdbc:postgresql://192.168.2.127:15432/kamco_cds
|
url: jdbc:postgresql://192.168.2.127:15432/kamco_cds
|
||||||
@@ -83,24 +86,25 @@ mapsheet:
|
|||||||
upload:
|
upload:
|
||||||
skipGdalValidation: true
|
skipGdalValidation: true
|
||||||
shp:
|
shp:
|
||||||
baseurl: /app/tmp/detect/result
|
baseurl: /app/tmp/detect/result #현재사용안함
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
file:
|
file:
|
||||||
#sync-root-dir: D:/kamco-nfs/images/
|
#sync-root-dir: D:/kamco-nfs/images/
|
||||||
sync-root-dir: /kamco-nfs/images/
|
sync-root-dir: /kamco-nfs/images/
|
||||||
sync-tmp-dir: ${file.sync-root-dir}/tmp
|
sync-tmp-dir: /kamco-nfs/requests/temp # image upload temp dir
|
||||||
|
#sync-tmp-dir: ${file.sync-root-dir}/tmp
|
||||||
sync-file-extention: tfw,tif
|
sync-file-extention: tfw,tif
|
||||||
sync-auto-exception-start-year: 2024
|
sync-auto-exception-start-year: 2024
|
||||||
sync-auto-exception-before-year-cnt: 3
|
sync-auto-exception-before-year-cnt: 3
|
||||||
|
|
||||||
#dataset-dir: D:/kamco-nfs/dataset/
|
#dataset-dir: D:/kamco-nfs/model_output/
|
||||||
dataset-dir: /kamco-nfs/dataset/export/
|
dataset-dir: /kamco-nfs/model_output/export/ # 마운트경로 AI 추론결과
|
||||||
dataset-tmp-dir: ${file.dataset-dir}tmp/
|
dataset-tmp-dir: ${file.dataset-dir}tmp/
|
||||||
|
|
||||||
#model-dir: D:/kamco-nfs/ckpt/model/
|
#model-dir: D:/kamco-nfs/ckpt/model/
|
||||||
model-dir: /kamco-nfs/ckpt/model/
|
model-dir: /kamco-nfs/ckpt/model/ # 학습서버에서 트레이닝한 모델업로드경로
|
||||||
model-tmp-dir: ${file.model-dir}tmp/
|
model-tmp-dir: ${file.model-dir}tmp/
|
||||||
model-file-extention: pth,json,py
|
model-file-extention: pth,json,py
|
||||||
|
|
||||||
@@ -112,8 +116,8 @@ file:
|
|||||||
inference:
|
inference:
|
||||||
url: http://192.168.2.183:8000/jobs
|
url: http://192.168.2.183:8000/jobs
|
||||||
batch-url: http://192.168.2.183:8000/batches
|
batch-url: http://192.168.2.183:8000/batches
|
||||||
geojson-dir: /kamco-nfs/requests/
|
geojson-dir: /kamco-nfs/requests/ # 추론실행을 위한 파일생성경로
|
||||||
jar-path: /kamco-nfs/dataset/shp/shp-exporter.jar
|
jar-path: /kamco-nfs/repo/jar/shp-exporter.jar
|
||||||
inference-server-name: server1,server2,server3,server4
|
inference-server-name: server1,server2,server3,server4
|
||||||
|
|
||||||
gukyuin:
|
gukyuin:
|
||||||
@@ -122,7 +126,7 @@ gukyuin:
|
|||||||
cdi: ${gukyuin.url}/api/kcd/cdi
|
cdi: ${gukyuin.url}/api/kcd/cdi
|
||||||
|
|
||||||
training-data:
|
training-data:
|
||||||
geojson-dir: /kamco-nfs/model_output/labeling/
|
geojson-dir: /kamco-nfs/dataset/request/
|
||||||
|
|
||||||
layer:
|
layer:
|
||||||
geoserver-url: https://kamco.geo-dev.gs.dabeeo.com
|
geoserver-url: https://kamco.geo-dev.gs.dabeeo.com
|
||||||
|
|||||||
@@ -16,14 +16,43 @@ spring:
|
|||||||
format_sql: true # ⚠️ 선택 - SQL 포맷팅 (가독성)
|
format_sql: true # ⚠️ 선택 - SQL 포맷팅 (가독성)
|
||||||
jdbc:
|
jdbc:
|
||||||
batch_size: 1000 # ✅ 추가 (JDBC batch)
|
batch_size: 1000 # ✅ 추가 (JDBC batch)
|
||||||
|
mvc:
|
||||||
|
async:
|
||||||
|
request-timeout: 300s # 5분 (예: 30s, 120s, 10m 등도 가능)
|
||||||
|
|
||||||
datasource:
|
datasource:
|
||||||
url: jdbc:postgresql://10.100.0.10:25432/temp
|
url: jdbc:postgresql://127.0.0.1:15432/kamco_cds
|
||||||
username: temp
|
#url: jdbc:postgresql://localhost:15432/kamco_cds
|
||||||
password: temp123!
|
username: kamco_cds
|
||||||
|
password: kamco_cds_Q!W@E#R$
|
||||||
hikari:
|
hikari:
|
||||||
minimum-idle: 10
|
minimum-idle: 10
|
||||||
maximum-pool-size: 20
|
maximum-pool-size: 20
|
||||||
|
connection-timeout: 60000 # 60초 연결 타임아웃
|
||||||
|
idle-timeout: 300000 # 5분 유휴 타임아웃
|
||||||
|
max-lifetime: 1800000 # 30분 최대 수명
|
||||||
|
leak-detection-threshold: 60000 # 연결 누수 감지
|
||||||
|
|
||||||
|
transaction:
|
||||||
|
default-timeout: 300 # 5분 트랜잭션 타임아웃
|
||||||
|
|
||||||
|
data:
|
||||||
|
redis:
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 16379
|
||||||
|
password: kamco
|
||||||
|
|
||||||
|
servlet:
|
||||||
|
multipart:
|
||||||
|
enabled: true
|
||||||
|
max-file-size: 4GB
|
||||||
|
max-request-size: 4GB
|
||||||
|
file-size-threshold: 10MB
|
||||||
|
|
||||||
|
server:
|
||||||
|
tomcat:
|
||||||
|
max-swallow-size: 4GB
|
||||||
|
max-http-form-post-size: 4GB
|
||||||
|
|
||||||
jwt:
|
jwt:
|
||||||
secret: "kamco_token_9b71e778-19a3-4c1d-97bf-2d687de17d5b"
|
secret: "kamco_token_9b71e778-19a3-4c1d-97bf-2d687de17d5b"
|
||||||
@@ -34,28 +63,39 @@ token:
|
|||||||
refresh-cookie-name: kamco # 개발용 쿠키 이름
|
refresh-cookie-name: kamco # 개발용 쿠키 이름
|
||||||
refresh-cookie-secure: true # 로컬 http 테스트면 false
|
refresh-cookie-secure: true # 로컬 http 테스트면 false
|
||||||
|
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
root: INFO
|
||||||
|
org.springframework.web: DEBUG
|
||||||
|
org.springframework.security: DEBUG
|
||||||
|
|
||||||
|
# 헬스체크 노이즈 핵심만 다운
|
||||||
|
org.springframework.security.web.FilterChainProxy: INFO
|
||||||
|
org.springframework.security.web.authentication.AnonymousAuthenticationFilter: INFO
|
||||||
|
org.springframework.security.web.authentication.Http403ForbiddenEntryPoint: INFO
|
||||||
|
org.springframework.web.servlet.DispatcherServlet: INFO
|
||||||
|
|
||||||
|
|
||||||
mapsheet:
|
mapsheet:
|
||||||
upload:
|
upload:
|
||||||
skipGdalValidation: true
|
skipGdalValidation: true
|
||||||
shp:
|
shp:
|
||||||
baseurl: /app/detect/result
|
baseurl: /app/detect/result #현재사용안함
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
file:
|
file:
|
||||||
#sync-root-dir: D:/kamco-nfs/images/
|
#sync-root-dir: D:/kamco-nfs/images/
|
||||||
sync-root-dir: /kamco-nfs/images/
|
sync-root-dir: /kamco-nfs/images/
|
||||||
sync-tmp-dir: ${file.sync-root-dir}/tmp
|
sync-tmp-dir: ${file.sync-root-dir}/tmp # image upload temp dir
|
||||||
sync-file-extention: tfw,tif
|
sync-file-extention: tfw,tif
|
||||||
sync-auto-exception-start-year: 2025
|
sync-auto-exception-start-year: 2025
|
||||||
sync-auto-exception-before-year-cnt: 3
|
sync-auto-exception-before-year-cnt: 3
|
||||||
|
|
||||||
#dataset-dir: D:/kamco-nfs/dataset/
|
#dataset-dir: D:/kamco-nfs/model_output/ #변경 model_output
|
||||||
dataset-dir: /kamco-nfs/dataset/export/
|
dataset-dir: /kamco-nfs/model_output/export/ # 마운트경로 AI 추론결과
|
||||||
dataset-tmp-dir: ${file.dataset-dir}tmp/
|
dataset-tmp-dir: ${file.dataset-dir}tmp/
|
||||||
|
|
||||||
#model-dir: D:/kamco-nfs/ckpt/model/
|
#model-dir: D:/kamco-nfs/ckpt/model/
|
||||||
model-dir: /kamco-nfs/ckpt/model/
|
model-dir: /kamco-nfs/ckpt/model/ # 학습서버에서 트레이닝한 모델업로드경로
|
||||||
model-tmp-dir: ${file.model-dir}tmp/
|
model-tmp-dir: ${file.model-dir}tmp/
|
||||||
model-file-extention: pth,json,py
|
model-file-extention: pth,json,py
|
||||||
|
|
||||||
@@ -65,19 +105,18 @@ file:
|
|||||||
dataset-response: /kamco-nfs/dataset/response/
|
dataset-response: /kamco-nfs/dataset/response/
|
||||||
|
|
||||||
inference:
|
inference:
|
||||||
url: http://192.168.2.183:8000/jobs
|
url: http://127.0.0.1:8000/jobs
|
||||||
batch-url: http://192.168.2.183:8000/batches
|
batch-url: http://127.0.0.1:8000/batches
|
||||||
geojson-dir: /kamco-nfs/requests/
|
geojson-dir: /kamco-nfs/requests/ # 학습서버에서 트레이닝한 모델업로드경로
|
||||||
jar-path: /kamco-nfs/dataset/shp/shp-exporter.jar
|
jar-path: /kamco-nfs/repo/jar/shp-exporter.jar # 추론실행을 위한 파일생성경로
|
||||||
inference-server-name: server1,server2,server3,server4
|
inference-server-name: server1,server2,server3,server4
|
||||||
|
|
||||||
gukyuin:
|
gukyuin:
|
||||||
#url: http://localhost:8080
|
url: http://127.0.0.1:5301
|
||||||
url: http://192.168.2.129:5301
|
|
||||||
cdi: ${gukyuin.url}/api/kcd/cdi
|
cdi: ${gukyuin.url}/api/kcd/cdi
|
||||||
|
|
||||||
training-data:
|
training-data:
|
||||||
geojson-dir: /kamco-nfs/model_output/labeling/
|
geojson-dir: /kamco-nfs/dataset/request/
|
||||||
|
|
||||||
layer:
|
layer:
|
||||||
geoserver-url: https://kamco.geo-dev.gs.dabeeo.com
|
geoserver-url: https://kamco.geo-dev.gs.dabeeo.com
|
||||||
|
|||||||
63
src/main/resources/static/download_progress_test.html
Normal file
63
src/main/resources/static/download_progress_test.html
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ko">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>라벨 ZIP 다운로드</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h3>라벨 ZIP 다운로드</h3>
|
||||||
|
|
||||||
|
UUID:
|
||||||
|
<input id="uuid" value="6d8d49dc-0c9d-4124-adc7-b9ca610cc394" />
|
||||||
|
<button onclick="download()">다운로드</button>
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
<progress id="bar" value="0" max="100" style="width:400px;"></progress>
|
||||||
|
<div id="status"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
async function download() {
|
||||||
|
const uuid = document.getElementById("uuid").value.trim();
|
||||||
|
const url = `/api/training-data/stage/download/${uuid}`;
|
||||||
|
|
||||||
|
const res = await fetch(url, {
|
||||||
|
headers: {
|
||||||
|
"Authorization": `Bearer eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiJmYzUwNDYxZC0wOGYwLTRhYjYtOTg4MC03ZmJmYzM2ZjNmOGIiLCJpYXQiOjE3NzA3Nzg3ODIsImV4cCI6MTc3MDg2NTE4Mn0.4OOF4r2lxymr8UwpqRW_qLecE8IpwNOFJHXSsZX6d6Yuk2fT7NcYPS5LenFRli3N`,
|
||||||
|
"kamco-download-uuid": uuid
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
document.getElementById("status").innerText =
|
||||||
|
"실패: " + res.status;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const total = parseInt(res.headers.get("Content-Length") || "0", 10);
|
||||||
|
const reader = res.body.getReader();
|
||||||
|
const chunks = [];
|
||||||
|
let received = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
if (done) break;
|
||||||
|
chunks.push(value);
|
||||||
|
received += value.length;
|
||||||
|
|
||||||
|
if (total) {
|
||||||
|
document.getElementById("bar").value =
|
||||||
|
(received / total) * 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const blob = new Blob(chunks);
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = URL.createObjectURL(blob);
|
||||||
|
a.download = uuid + ".zip";
|
||||||
|
a.click();
|
||||||
|
|
||||||
|
document.getElementById("status").innerText = "완료 ✅";
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user