|
|
|
@ -1,17 +1,30 @@
|
|
|
|
package kr.re.etri.autoflow.service;
|
|
|
|
package kr.re.etri.autoflow.service;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import io.minio.MinioClient;
|
|
|
|
|
|
|
|
import io.minio.PutObjectArgs;
|
|
|
|
|
|
|
|
import kr.re.etri.autoflow.entity.MinioAttachmentEntity;
|
|
|
|
|
|
|
|
import kr.re.etri.autoflow.repository.MinioAttachmentRepository;
|
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
|
import org.springframework.core.io.ByteArrayResource;
|
|
|
|
import org.springframework.core.io.ByteArrayResource;
|
|
|
|
|
|
|
|
import org.springframework.core.io.InputStreamResource;
|
|
|
|
|
|
|
|
import org.springframework.core.io.buffer.DataBuffer;
|
|
|
|
|
|
|
|
import org.springframework.core.io.buffer.DataBufferUtils;
|
|
|
|
import org.springframework.http.*;
|
|
|
|
import org.springframework.http.*;
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import org.springframework.web.client.RestTemplate;
|
|
|
|
import org.springframework.web.client.RestTemplate;
|
|
|
|
import org.springframework.web.reactive.function.client.WebClient;
|
|
|
|
import org.springframework.web.reactive.function.client.WebClient;
|
|
|
|
import org.springframework.web.util.UriComponentsBuilder;
|
|
|
|
import org.springframework.web.util.UriComponentsBuilder;
|
|
|
|
|
|
|
|
import reactor.core.publisher.Flux;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import java.io.*;
|
|
|
|
import java.nio.charset.StandardCharsets;
|
|
|
|
import java.nio.charset.StandardCharsets;
|
|
|
|
|
|
|
|
import java.nio.file.Files;
|
|
|
|
|
|
|
|
import java.nio.file.Path;
|
|
|
|
import java.util.*;
|
|
|
|
import java.util.*;
|
|
|
|
|
|
|
|
import java.util.concurrent.CompletableFuture;
|
|
|
|
|
|
|
|
import java.util.concurrent.CountDownLatch;
|
|
|
|
|
|
|
|
|
|
|
|
@Service
|
|
|
|
@Service
|
|
|
|
@RequiredArgsConstructor
|
|
|
|
@RequiredArgsConstructor
|
|
|
|
@ -20,9 +33,21 @@ public class DatasetService {
|
|
|
|
|
|
|
|
|
|
|
|
private final RestTemplate restTemplate;
|
|
|
|
private final RestTemplate restTemplate;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final MinioAttachmentRepository minioAttachmentRepository;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static final String BASE_URL = "http://52.14.11.43:18010";
|
|
|
|
private static final String BASE_URL = "http://52.14.11.43:18010";
|
|
|
|
private static final String LIST_ENDPOINT = "/export/dataset_list";
|
|
|
|
private static final String LIST_ENDPOINT = "/export/dataset_list";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final MinioClient minioClient;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Value("${minio.bucket}")
|
|
|
|
|
|
|
|
private String bucketName;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Value("${minio.endpoint}")
|
|
|
|
|
|
|
|
private String minioEndpoint;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
public Map<String, Object> getDatasetList(Integer ds_prj_idx, String search_keyword, String grp_name) {
|
|
|
|
public Map<String, Object> getDatasetList(Integer ds_prj_idx, String search_keyword, String grp_name) {
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
@ -86,48 +111,110 @@ public class DatasetService {
|
|
|
|
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
|
|
|
|
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
|
|
|
|
.build();
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
|
|
public ResponseEntity<?> downloadDataset(String datasetName) {
|
|
|
|
public MinioAttachmentEntity downloadDataset(
|
|
|
|
|
|
|
|
String datasetName,
|
|
|
|
|
|
|
|
String path,
|
|
|
|
|
|
|
|
Long refId,
|
|
|
|
|
|
|
|
String refType,
|
|
|
|
|
|
|
|
String title,
|
|
|
|
|
|
|
|
String description,
|
|
|
|
|
|
|
|
Integer version,
|
|
|
|
|
|
|
|
String regUserId,
|
|
|
|
|
|
|
|
Long projectId
|
|
|
|
|
|
|
|
) {
|
|
|
|
String apiUrl = baseUrl + DOWNLOAD_ENDPOINT;
|
|
|
|
String apiUrl = baseUrl + DOWNLOAD_ENDPOINT;
|
|
|
|
log.info("외부 API 요청 URL: {}", apiUrl);
|
|
|
|
log.info("외부 API 요청 URL: {}", apiUrl);
|
|
|
|
log.info("요청 데이터셋 이름: {}", datasetName);
|
|
|
|
log.info("요청 데이터셋 이름: {}", datasetName);
|
|
|
|
|
|
|
|
|
|
|
|
// 요청 본문(JSON)
|
|
|
|
String storedName = UUID.randomUUID() + "-" + datasetName + ".zip";
|
|
|
|
String requestBody = String.format("{\"dataset_name\":\"%s\"}", datasetName);
|
|
|
|
String objectName = (path == null || path.isEmpty()) ? storedName : path + "/" + storedName;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
// WebClient로 외부 API POST 요청
|
|
|
|
Flux<DataBuffer> dataBufferFlux = webClient.post()
|
|
|
|
return webClient.post()
|
|
|
|
|
|
|
|
.uri(apiUrl)
|
|
|
|
.uri(apiUrl)
|
|
|
|
.bodyValue(requestBody)
|
|
|
|
.bodyValue(Map.of("dataset_name", datasetName))
|
|
|
|
.exchangeToMono(response -> {
|
|
|
|
.retrieve()
|
|
|
|
if (response.statusCode().equals(HttpStatus.OK)) {
|
|
|
|
.bodyToFlux(DataBuffer.class)
|
|
|
|
// 파일 스트림 수신
|
|
|
|
.doOnError(e -> log.error("다운로드 중 오류 발생", e));
|
|
|
|
return response.bodyToMono(byte[].class)
|
|
|
|
|
|
|
|
.map(bytes -> {
|
|
|
|
PipedOutputStream pos = new PipedOutputStream();
|
|
|
|
ByteArrayResource resource = new ByteArrayResource(bytes);
|
|
|
|
PipedInputStream pis = new PipedInputStream(pos, 10 * 1024 * 1024);
|
|
|
|
HttpHeaders headers = new HttpHeaders();
|
|
|
|
|
|
|
|
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
|
|
|
|
CountDownLatch latch = new CountDownLatch(1);
|
|
|
|
headers.setContentDisposition(ContentDisposition.attachment()
|
|
|
|
|
|
|
|
.filename(datasetName + ".zip", StandardCharsets.UTF_8)
|
|
|
|
// 파일 크기 저장용
|
|
|
|
.build());
|
|
|
|
long[] totalBytes = {0};
|
|
|
|
return new ResponseEntity<>(resource, headers, HttpStatus.OK);
|
|
|
|
|
|
|
|
});
|
|
|
|
Thread uploadThread = new Thread(() -> {
|
|
|
|
} else {
|
|
|
|
try {
|
|
|
|
// 오류 응답 처리
|
|
|
|
minioClient.putObject(
|
|
|
|
return response.bodyToMono(String.class)
|
|
|
|
PutObjectArgs.builder()
|
|
|
|
.map(errorBody -> {
|
|
|
|
.bucket(bucketName)
|
|
|
|
log.error("다운로드 실패 (HTTP {}): {}", response.statusCode(), errorBody);
|
|
|
|
.object(objectName)
|
|
|
|
return ResponseEntity.status(response.statusCode())
|
|
|
|
.stream(pis, -1, 10 * 1024 * 1024)
|
|
|
|
.contentType(MediaType.APPLICATION_JSON)
|
|
|
|
.contentType("application/zip")
|
|
|
|
.body(errorBody);
|
|
|
|
.build()
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
log.info("MinIO 업로드 완료: {}", objectName);
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
log.error("MinIO 업로드 실패", e);
|
|
|
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
|
|
try { pis.close(); } catch (Exception ignored) {}
|
|
|
|
|
|
|
|
latch.countDown();
|
|
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
uploadThread.start();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dataBufferFlux.subscribe(
|
|
|
|
|
|
|
|
buffer -> {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
byte[] bytes = new byte[buffer.readableByteCount()];
|
|
|
|
|
|
|
|
buffer.read(bytes);
|
|
|
|
|
|
|
|
pos.write(bytes);
|
|
|
|
|
|
|
|
pos.flush();
|
|
|
|
|
|
|
|
totalBytes[0] += bytes.length;
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
|
|
DataBufferUtils.release(buffer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
},
|
|
|
|
.block();
|
|
|
|
error -> {
|
|
|
|
|
|
|
|
log.error("다운로드 중 오류 발생", error);
|
|
|
|
|
|
|
|
try { pos.close(); } catch (Exception ignored) {}
|
|
|
|
|
|
|
|
latch.countDown();
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
() -> {
|
|
|
|
|
|
|
|
try { pos.close(); } catch (Exception ignored) {}
|
|
|
|
|
|
|
|
latch.countDown();
|
|
|
|
|
|
|
|
log.info("다운로드 완료: {} MB", totalBytes[0] / (1024 * 1024));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
latch.await();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// DB 저장 시 size 컬럼 필수
|
|
|
|
|
|
|
|
MinioAttachmentEntity attachment = MinioAttachmentEntity.builder()
|
|
|
|
|
|
|
|
.refId(refId)
|
|
|
|
|
|
|
|
.refType(refType)
|
|
|
|
|
|
|
|
.originalName(datasetName + ".zip")
|
|
|
|
|
|
|
|
.storedName(storedName)
|
|
|
|
|
|
|
|
.contentType("application/zip")
|
|
|
|
|
|
|
|
.storagePath(objectName)
|
|
|
|
|
|
|
|
.title(title != null ? title : datasetName)
|
|
|
|
|
|
|
|
.version(version)
|
|
|
|
|
|
|
|
.description(description)
|
|
|
|
|
|
|
|
.regUserId(regUserId)
|
|
|
|
|
|
|
|
.projectId(projectId)
|
|
|
|
|
|
|
|
.size(totalBytes[0])
|
|
|
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return minioAttachmentRepository.save(attachment);
|
|
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
} catch (Exception e) {
|
|
|
|
log.error("API 요청 중 오류 발생", e);
|
|
|
|
log.error("외부 API 다운로드 및 MinIO 업로드 실패", e);
|
|
|
|
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
|
|
|
throw new RuntimeException("데이터셋 저장 실패: " + e.getMessage(), e);
|
|
|
|
.body("API 요청 중 오류 발생: " + e.getMessage());
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|