Files
no-copy/src/main/java/ru/soune/nocopy/service/file/ZipService.java
T

159 lines
5.2 KiB
Java
Raw Normal View History

package ru.soune.nocopy.service.file;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
2026-04-23 00:38:40 +07:00
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import ru.soune.nocopy.entity.file.FileEntity;
import ru.soune.nocopy.entity.file.moderation.ModerationPassportFile;
2026-04-23 00:38:40 +07:00
import ru.soune.nocopy.service.file.cloud.CloudStorageService;
import ru.soune.nocopy.service.file.moderation.ModerationFileService;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@Service
@Slf4j
@RequiredArgsConstructor
public class ZipService {
private final ModerationFileService moderationFileService;
2026-04-23 00:38:40 +07:00
private final CloudStorageService cloudStorageService;
private static final int MAX_SIZE_MB = 20;
private static final int FIRST_PART = 1;
private static final int SECOND_PART = 2;
2026-04-23 00:38:40 +07:00
@Value("${yandex.cloud.bucket-cold}")
private String bucketCold;
public void createAndSplitZip(Long userId, List<FileEntity> files) throws IOException {
byte[] zipBytes = createZip(files);
String base64String = Base64.getEncoder().encodeToString(zipBytes);
String[] parts = splitBase64(base64String);
saveSplitFiles(userId, parts[0], parts[1]);
}
public byte[] assembleZipFromSplitFiles(Long userId) throws IOException {
2026-04-23 00:38:40 +07:00
List<ModerationPassportFile> activeFiles = moderationFileService.getActiveFilesByUserId(userId);
2026-04-23 00:38:40 +07:00
String part1Key = null;
String part2Key = null;
for (ModerationPassportFile file : activeFiles) {
if (file.getPart() == FIRST_PART) {
2026-04-23 00:38:40 +07:00
part1Key = file.getPath();
} else if (file.getPart() == SECOND_PART) {
2026-04-23 00:38:40 +07:00
part2Key = file.getPath();
}
}
2026-04-23 00:38:40 +07:00
if (part1Key == null || part2Key == null) {
throw new IOException("Missing part 1 or part 2 for user: " + userId);
}
2026-04-23 00:38:40 +07:00
String part1Content = cloudStorageService.downloadPartFromBucket(bucketCold, part1Key);
String part2Content = cloudStorageService.downloadPartFromBucket(bucketCold, part2Key);
2026-04-23 00:38:40 +07:00
String fullBase64 = part1Content + part2Content;
return Base64.getDecoder().decode(fullBase64);
}
private byte[] createZip(List<FileEntity> files) throws IOException {
long totalSize = files.stream()
.map(FileEntity::getFileSize)
.reduce(0L, Long::sum);
if (totalSize > MAX_SIZE_MB * 1024 * 1024) {
throw new IllegalArgumentException(
String.format("Total size %.2f MB exceeds %d MB limit",
totalSize / (1024.0 * 1024.0), MAX_SIZE_MB)
);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ZipOutputStream zos = new ZipOutputStream(baos)) {
for (FileEntity file: files) {
String filePath = file.getFilePath();
String fileName = file.getStoredFileName();
Path path = Paths.get(filePath);
ZipEntry zipEntry = new ZipEntry(fileName);
zos.putNextEntry(zipEntry);
Files.copy(path, zos);
zos.closeEntry();
log.info("Added file {} to ZIP", fileName);
}
}
return baos.toByteArray();
}
private String[] splitBase64(String base64) {
StringBuilder part1 = new StringBuilder();
StringBuilder part2 = new StringBuilder();
for (int i = 0; i < base64.length(); i++) {
if (i % 2 == 0) {
part1.append(base64.charAt(i));
} else {
part2.append(base64.charAt(i));
}
}
return new String[]{part1.toString(), part2.toString()};
}
private void saveSplitFiles(Long userId, String part1, String part2) throws IOException {
Path userDir = Paths.get("/uploads/passport", String.valueOf(userId));
Files.createDirectories(userDir);
Path file1 = userDir.resolve(userId + "___1.txt");
Files.writeString(file1, part1);
Path file2 = userDir.resolve(userId + "___2.txt");
Files.writeString(file2, part2);
2026-04-23 00:38:40 +07:00
Map<String, String> cloudKeys = cloudStorageService.saveSplitPartsToColdStorage(userId, file1, file2);
Map<Integer, String> files = Map.of(
FIRST_PART, cloudKeys.get("part1"),
SECOND_PART, cloudKeys.get("part2"));
moderationFileService.addFiles(userId, files);
2026-04-23 00:38:40 +07:00
log.info("Saved split files to cold storage for userId: {}", userId);
}
private String mergeBase64(String part1, String part2) {
StringBuilder merged = new StringBuilder();
int maxLength = Math.max(part1.length(), part2.length());
for (int i = 0; i < maxLength; i++) {
if (i < part1.length()) {
merged.append(part1.charAt(i));
}
if (i < part2.length()) {
merged.append(part2.charAt(i));
}
}
return merged.toString();
}
}