Merge remote-tracking branch 'origin/feat/dev_251201' into feat/dev_251201

This commit is contained in:
2025-12-26 16:44:17 +09:00
7 changed files with 208 additions and 0 deletions

View File

@@ -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 파일 읽어서 작업 필요할 때 정의하기
}
});
}
}
}

View File

@@ -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();
}
}
}
}
}

View File

@@ -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 파일 읽어서 작업할 때 정의하기
}
}
}

View File

@@ -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;
}

View File

@@ -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<ZipEntryProcessor> processors;
public ZipUtils(List<ZipEntryProcessor> 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();
}
}
}
}

View File

@@ -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<Page<ModelMngDto.ModelList>> 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());
}
}