diff --git a/src/main/java/com/kamco/cd/kamcoback/common/utils/zip/CsvFileProcessor.java b/src/main/java/com/kamco/cd/kamcoback/common/utils/zip/CsvFileProcessor.java new file mode 100644 index 00000000..fc440c58 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/common/utils/zip/CsvFileProcessor.java @@ -0,0 +1,33 @@ +package com.kamco.cd.kamcoback.common.utils.zip; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class CsvFileProcessor implements ZipEntryProcessor { + + @Override + public boolean supports(String fileName) { + return fileName.toLowerCase().endsWith(".csv"); + } + + @Override + public void process(String fileName, InputStream is) throws IOException { + try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) { + br.lines() + .forEach( + line -> { + String[] cols = line.split(","); + // CSV 처리 + for (String col : cols) { + log.info(col); // TODO : 추후에 csv 파일 읽어서 작업 필요할 때 정의하기 + } + }); + } + } +} diff --git a/src/main/java/com/kamco/cd/kamcoback/common/utils/zip/JsonStreamingFileProcessor.java b/src/main/java/com/kamco/cd/kamcoback/common/utils/zip/JsonStreamingFileProcessor.java new file mode 100644 index 00000000..40d2e423 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/common/utils/zip/JsonStreamingFileProcessor.java @@ -0,0 +1,73 @@ +package com.kamco.cd.kamcoback.common.utils.zip; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.io.InputStream; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class JsonStreamingFileProcessor implements ZipEntryProcessor { + + private final JsonFactory jsonFactory; + + public JsonStreamingFileProcessor(ObjectMapper objectMapper) { + // ZipInputStream 보호용 설정 + objectMapper.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, false); + this.jsonFactory = objectMapper.getFactory(); + } + + @Override + public boolean supports(String fileName) { + return fileName.toLowerCase().endsWith(".json"); + } + + @Override + public void process(String fileName, InputStream is) throws IOException { + + log.info("JSON process start: {}", fileName); + + JsonParser parser = jsonFactory.createParser(is); + + // JSON 구조에 상관없이 token 단위로 순회 + while (parser.nextToken() != null) { + handleToken(parser); + } + + log.info("JSON process end: {}", fileName); + } + + private void handleToken(JsonParser parser) throws IOException { + JsonToken token = parser.currentToken(); + + if (token == JsonToken.FIELD_NAME) { + String fieldName = parser.getCurrentName(); + // TODO: json 파일 읽어야 할 내용 정의되면 항목 확정하기 + switch (fieldName) { + case "type" -> { + parser.nextToken(); + String type = parser.getValueAsString(); + log.info("type: {}", type); + } + case "name" -> { + parser.nextToken(); + String name = parser.getValueAsString(); + log.info("Name: {}", name); + } + case "features" -> { + parser.nextToken(); + String features = parser.readValueAsTree().toString(); + log.info("features: {}", features); + } + default -> { + parser.nextToken(); + parser.skipChildren(); + } + } + } + } +} diff --git a/src/main/java/com/kamco/cd/kamcoback/common/utils/zip/TextFileProcessor.java b/src/main/java/com/kamco/cd/kamcoback/common/utils/zip/TextFileProcessor.java new file mode 100644 index 00000000..1f5d0b5f --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/common/utils/zip/TextFileProcessor.java @@ -0,0 +1,27 @@ +package com.kamco.cd.kamcoback.common.utils.zip; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class TextFileProcessor implements ZipEntryProcessor { + + @Override + public boolean supports(String fileName) { + return fileName.toLowerCase().endsWith(".txt"); + } + + @Override + public void process(String fileName, InputStream is) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String line; + while ((line = br.readLine()) != null) { + log.info(line); // TODO : 추후 txt 파일 읽어서 작업할 때 정의하기 + } + } +} diff --git a/src/main/java/com/kamco/cd/kamcoback/common/utils/zip/ZipEntryProcessor.java b/src/main/java/com/kamco/cd/kamcoback/common/utils/zip/ZipEntryProcessor.java new file mode 100644 index 00000000..985b6a8b --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/common/utils/zip/ZipEntryProcessor.java @@ -0,0 +1,11 @@ +package com.kamco.cd.kamcoback.common.utils.zip; + +import java.io.IOException; +import java.io.InputStream; + +public interface ZipEntryProcessor { + + boolean supports(String fileName); + + void process(String fileName, InputStream is) throws IOException; +} diff --git a/src/main/java/com/kamco/cd/kamcoback/common/utils/zip/ZipUtils.java b/src/main/java/com/kamco/cd/kamcoback/common/utils/zip/ZipUtils.java new file mode 100644 index 00000000..c0e7f5ed --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/common/utils/zip/ZipUtils.java @@ -0,0 +1,49 @@ +package com.kamco.cd.kamcoback.common.utils.zip; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class ZipUtils { + + private final List processors; + + public ZipUtils(List processors) { + this.processors = processors; + } + + public void processZip(InputStream zipStream) throws IOException { + try (ZipInputStream zis = new ZipInputStream(zipStream)) { + + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + + if (entry.isDirectory()) { + continue; + } + + String fileName = entry.getName(); + processors.stream() + .filter(p -> p.supports(fileName)) + .findFirst() + .ifPresent( + processor -> { + try { + processor.process(fileName, zis); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + }); + + zis.closeEntry(); + } + } + } +} diff --git a/src/main/java/com/kamco/cd/kamcoback/model/ModelMngApiController.java b/src/main/java/com/kamco/cd/kamcoback/model/ModelMngApiController.java index 802a24e1..1dcb01b6 100644 --- a/src/main/java/com/kamco/cd/kamcoback/model/ModelMngApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/model/ModelMngApiController.java @@ -1,5 +1,6 @@ package com.kamco.cd.kamcoback.model; +import com.kamco.cd.kamcoback.common.utils.zip.ZipUtils; import com.kamco.cd.kamcoback.config.api.ApiResponseDto; import com.kamco.cd.kamcoback.model.dto.ModelMngDto; import com.kamco.cd.kamcoback.model.service.ModelMngService; @@ -10,15 +11,21 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.transaction.Transactional; +import java.io.IOException; import java.time.LocalDate; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; +import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.DeleteMapping; 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.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; @Tag(name = "모델 관리", description = "모델 관리 API") @RequiredArgsConstructor @@ -29,6 +36,8 @@ public class ModelMngApiController { private final ModelMngService modelMngService; + @Autowired private ZipUtils zipUtils; + @Operation(summary = "모델관리 목록") @GetMapping public ApiResponseDto> findModelMgmtList( @@ -70,4 +79,10 @@ public class ModelMngApiController { String modelVer) { return ApiResponseDto.okObject(modelMngService.removeModel(modelVer)); } + + @Operation(summary = "모델 zip 파일 업로드", description = "모델 zip 파일 업로드") + @PostMapping(value = "/upload/zip", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public void upload(@RequestPart MultipartFile zipFilie) throws IOException { + zipUtils.processZip(zipFilie.getInputStream()); + } } diff --git a/src/main/resources/db/migration/dump-kamco_cds-202512231534.tar b/src/main/resources/db/migration/dump-kamco_cds-202512261634.tar similarity index 76% rename from src/main/resources/db/migration/dump-kamco_cds-202512231534.tar rename to src/main/resources/db/migration/dump-kamco_cds-202512261634.tar index 5396379a..e7818316 100644 Binary files a/src/main/resources/db/migration/dump-kamco_cds-202512231534.tar and b/src/main/resources/db/migration/dump-kamco_cds-202512261634.tar differ