You've already forked DataMate
feat(data-management): 修改知识项导出功能为ZIP格式
- 将导出文件格式从JSON改为ZIP压缩包 - 使用ZipArchiveOutputStream实现ZIP文件创建 - 为每个知识项创建独立的文件条目 - 添加文件名规范化和长度限制逻辑 - 实现重复文件名的索引编号处理 - 移除Jackson ObjectMapper依赖引入 - 更新响应头内容类型为application/zip
This commit is contained in:
@@ -27,12 +27,13 @@ import com.datamate.datamanagement.interfaces.dto.ImportKnowledgeItemsRequest;
|
||||
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemPagingQuery;
|
||||
import com.datamate.datamanagement.interfaces.dto.KnowledgeItemResponse;
|
||||
import com.datamate.datamanagement.interfaces.dto.UpdateKnowledgeItemRequest;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -46,8 +47,10 @@ import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
@@ -63,8 +66,9 @@ public class KnowledgeItemApplicationService {
|
||||
private static final Set<String> SUPPORTED_TEXT_EXTENSIONS = Set.of("txt", "md", "markdown");
|
||||
private static final DateTimeFormatter EXPORT_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
|
||||
private static final String EXPORT_FILE_PREFIX = "knowledge_set_";
|
||||
private static final String EXPORT_FILE_SUFFIX = ".json";
|
||||
private static final String EXPORT_CONTENT_TYPE = "application/json";
|
||||
private static final String EXPORT_FILE_SUFFIX = ".zip";
|
||||
private static final String EXPORT_CONTENT_TYPE = "application/zip";
|
||||
private static final int MAX_FILE_BASE_LENGTH = 120;
|
||||
|
||||
private final KnowledgeItemRepository knowledgeItemRepository;
|
||||
private final KnowledgeSetRepository knowledgeSetRepository;
|
||||
@@ -228,16 +232,25 @@ public class KnowledgeItemApplicationService {
|
||||
BusinessAssert.notNull(response, CommonErrorCode.PARAM_ERROR);
|
||||
KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
|
||||
List<KnowledgeItem> items = knowledgeItemRepository.findAllBySetId(setId);
|
||||
List<KnowledgeItemResponse> responses = KnowledgeConverter.INSTANCE.convertItemResponses(items);
|
||||
|
||||
response.setContentType(EXPORT_CONTENT_TYPE);
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename=\"" + buildExportFileName(knowledgeSet.getId()) + "\"");
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
try {
|
||||
objectMapper.writeValue(response.getOutputStream(), responses);
|
||||
try (ZipArchiveOutputStream zos = new ZipArchiveOutputStream(response.getOutputStream())) {
|
||||
zos.setEncoding(StandardCharsets.UTF_8.name());
|
||||
Map<String, Integer> nameCounter = new HashMap<>();
|
||||
for (KnowledgeItem item : items) {
|
||||
String entryName = buildItemEntryName(item, nameCounter);
|
||||
ZipArchiveEntry entry = new ZipArchiveEntry(entryName);
|
||||
zos.putArchiveEntry(entry);
|
||||
byte[] contentBytes = (item.getContent() == null ? "" : item.getContent())
|
||||
.getBytes(StandardCharsets.UTF_8);
|
||||
zos.write(contentBytes);
|
||||
zos.closeArchiveEntry();
|
||||
}
|
||||
zos.finish();
|
||||
} catch (IOException e) {
|
||||
log.error("export knowledge items error, setId: {}", setId, e);
|
||||
throw BusinessException.of(SystemErrorCode.FILE_SYSTEM_ERROR);
|
||||
@@ -254,6 +267,50 @@ public class KnowledgeItemApplicationService {
|
||||
return EXPORT_FILE_PREFIX + setId + "_" + LocalDateTime.now().format(EXPORT_TIME_FORMATTER) + EXPORT_FILE_SUFFIX;
|
||||
}
|
||||
|
||||
private String buildItemEntryName(KnowledgeItem item, Map<String, Integer> nameCounter) {
|
||||
String rawTitle = StringUtils.isNotBlank(item.getTitle()) ? item.getTitle() : "item-" + item.getId();
|
||||
String baseName = sanitizeFileName(rawTitle);
|
||||
if (StringUtils.isBlank(baseName)) {
|
||||
baseName = "item-" + item.getId();
|
||||
}
|
||||
baseName = trimToLength(baseName, MAX_FILE_BASE_LENGTH);
|
||||
|
||||
String extension = item.getContentType() == KnowledgeContentType.MARKDOWN ? "md" : "txt";
|
||||
String baseKey = baseName + "." + extension;
|
||||
int count = nameCounter.getOrDefault(baseKey, 0);
|
||||
nameCounter.put(baseKey, count + 1);
|
||||
if (count == 0) {
|
||||
return baseKey;
|
||||
}
|
||||
return buildIndexedFileName(baseName, extension, count);
|
||||
}
|
||||
|
||||
private String buildIndexedFileName(String baseName, String extension, int index) {
|
||||
String suffix = "_" + index;
|
||||
int maxBaseLength = Math.max(1, MAX_FILE_BASE_LENGTH - suffix.length());
|
||||
String safeBase = trimToLength(baseName, maxBaseLength);
|
||||
return safeBase + suffix + "." + extension;
|
||||
}
|
||||
|
||||
private String sanitizeFileName(String name) {
|
||||
if (name == null) {
|
||||
return "";
|
||||
}
|
||||
String normalized = name.replaceAll("[\\\\/:*?\"<>|]", "_");
|
||||
normalized = normalized.replaceAll("\\s+", " ").trim();
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private String trimToLength(String value, int maxLength) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
if (value.length() <= maxLength) {
|
||||
return value;
|
||||
}
|
||||
return value.substring(0, maxLength);
|
||||
}
|
||||
|
||||
private KnowledgeContentType resolveContentType(DatasetFile datasetFile) {
|
||||
String extension = getFileExtension(datasetFile);
|
||||
if (StringUtils.isBlank(extension)) {
|
||||
|
||||
Reference in New Issue
Block a user