Compare commits
3
Commits
NCB-101
...
daef30ec11
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
daef30ec11 | ||
|
|
a25b33fe13 | ||
|
|
2985d84b04 |
@@ -151,3 +151,13 @@ INSERT INTO referral_levels (id, name, min_invitees, reward_percentage) VALUES
|
||||
('silver', 'SILVER', 6, 18),
|
||||
('gold', 'GOLD', 16, 22),
|
||||
('platinum', 'PLATINUM', 50, 25);
|
||||
|
||||
------
|
||||
|
||||
Настройка Яндекс Клауд
|
||||
|
||||
1. YCAJEpWAtaVkVGX0sH6_EupEg - индетификатор ключа
|
||||
2. YCMmVykfXrZ_nfU13Vo4yoCVGa70DnTlBgF1pUzO - секретный ключ
|
||||
|
||||
private static final String KEY_ID = "YCAJEmHZcWxzf4oGWvETG3mms";
|
||||
private static final String SECRET_KEY = "YCPAPZ32XC-LWb8xE0b_xTcBD8NZUCHpSOfu8LUG";
|
||||
@@ -65,6 +65,12 @@ dependencies {
|
||||
|
||||
implementation project(':referral')
|
||||
|
||||
//cloud
|
||||
implementation("software.amazon.awssdk:aws-sdk-java:2.29.33")
|
||||
implementation("software.amazon.awssdk:apache-client:2.29.33")
|
||||
|
||||
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0")
|
||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||
}
|
||||
|
||||
tasks.named('test') {
|
||||
|
||||
@@ -32,6 +32,7 @@ import ru.soune.nocopy.repository.AuthTokenRepository;
|
||||
import ru.soune.nocopy.repository.FileEntityRepository;
|
||||
import ru.soune.nocopy.service.FileSimilarityService;
|
||||
import ru.soune.nocopy.service.file.ProtectionsLimitService;
|
||||
import ru.soune.nocopy.service.file.cloud.CloudStorageService;
|
||||
import ru.soune.nocopy.service.register.AuthService;
|
||||
import ru.soune.nocopy.service.file.FileEntityService;
|
||||
import ru.soune.nocopy.service.file.FileUploadService;
|
||||
@@ -73,6 +74,8 @@ public class ApiController {
|
||||
|
||||
private final AuthTokenRepository authTokenRepository;
|
||||
|
||||
private final CloudStorageService cloudStorageService;
|
||||
|
||||
@PostMapping("/v{version}/data")
|
||||
public ResponseEntity<?> handlePostRequest(@RequestBody BaseRequest request,
|
||||
@PathVariable("version") int version) {
|
||||
@@ -342,29 +345,29 @@ public class ApiController {
|
||||
errorData));
|
||||
}
|
||||
|
||||
|
||||
Path filePath = Paths.get(entityResponse.getProtectedFilePath());
|
||||
// Resource resource = new UrlResource(filePath.toUri());
|
||||
FileSystemResource resource = new FileSystemResource(filePath);
|
||||
File file = cloudStorageService.readFileFromStorageByPath(
|
||||
entityResponse.getProtectedFilePath());
|
||||
long fileSize = Files.size(filePath);
|
||||
|
||||
if (!resource.exists()) {
|
||||
if (!file.exists()) {
|
||||
Map<String, Object> errorData = new HashMap<>();
|
||||
errorData.put("resource", resource.exists());
|
||||
errorData.put("file", file.exists());
|
||||
|
||||
return ResponseEntity.ok().body(new BaseResponse(20004,
|
||||
MessageCode.FILE_DOWNLOAD_ERROR.getCode(),
|
||||
MessageCode.FILE_DOWNLOAD_ERROR.getDescription(),
|
||||
errorData));
|
||||
}
|
||||
String contentType = determineContentType(filePath);
|
||||
|
||||
String contentType = determineContentType(filePath);
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.contentLength(fileSize)
|
||||
.contentType(MediaType.parseMediaType(contentType))
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename=\"" + entityResponse.getFileName() + "\"")
|
||||
.body(resource);
|
||||
.body(file);
|
||||
} catch (FileEntityNotFoundException e) {
|
||||
Map<String, Object> errorData = new HashMap<>();
|
||||
errorData.put("fileId", fileId);
|
||||
@@ -438,33 +441,6 @@ public class ApiController {
|
||||
return ResponseEntity.ok().body(fileTypeStats);
|
||||
}
|
||||
|
||||
private boolean hasDuplicate(List<SimilarFileDTO> similarFiles) {
|
||||
return similarFiles.stream().anyMatch(f -> f.getHammingDistance() <= 5);
|
||||
}
|
||||
|
||||
private ResponseEntity<BaseResponse> handleDuplicate(FileEntity fileEntity, List<SimilarFileDTO> similarFiles)
|
||||
throws IOException {
|
||||
fileEntityService.deleteFromDisk(fileEntity);
|
||||
|
||||
fileEntityService.softDeleteFileWithHash(fileEntity);
|
||||
|
||||
Optional<FileEntity> originalFile = fileEntityRepository.findById(similarFiles.get(0).getFileId());
|
||||
|
||||
if (originalFile.isPresent()) {
|
||||
Map<String, String> duplicateInfo = Map.of(
|
||||
"duplicate_file_id", originalFile.get().getId(),
|
||||
"owner_user_id", String.valueOf(originalFile.get().getUserId()));
|
||||
|
||||
return ResponseEntity.ok().body(new BaseResponse(
|
||||
20004,
|
||||
MessageCode.DUPLICATE_FILE_UPLOAD.getCode(),
|
||||
"Failed to upload chunk, duplicate",
|
||||
duplicateInfo));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private ResponseEntity<BaseResponse> buildSuccessResponse(String uploadId, Integer chunkNumber, MultipartFile chunk,
|
||||
String fileId) {
|
||||
ChunkUploadResponse responseBody = ChunkUploadResponse.builder()
|
||||
|
||||
@@ -12,6 +12,7 @@ import ru.soune.nocopy.entity.file.ProtectionStatus;
|
||||
import ru.soune.nocopy.exception.FileEntityNotFoundException;
|
||||
import ru.soune.nocopy.exception.NotFoundAuthToken;
|
||||
import ru.soune.nocopy.repository.FileEntityRepository;
|
||||
import ru.soune.nocopy.service.file.cloud.CloudStorageService;
|
||||
import ru.soune.nocopy.service.register.AuthService;
|
||||
import ru.soune.nocopy.service.file.FileEntityService;
|
||||
import ru.soune.nocopy.service.file.FileStatsService;
|
||||
@@ -35,6 +36,8 @@ public class FileEntityHandler implements RequestHandler {
|
||||
|
||||
private final FileEntityRepository fileEntityRepository;
|
||||
|
||||
private final CloudStorageService cloudStorageService;
|
||||
|
||||
@Override
|
||||
public BaseResponse handle(BaseRequest request) {
|
||||
try {
|
||||
@@ -302,7 +305,7 @@ public class FileEntityHandler implements RequestHandler {
|
||||
int fullDelete = fileRequest.getFullDelete() == null ? 0 : fileRequest.getFullDelete();
|
||||
|
||||
if (fileEntity.getStatus().equals(FileStatus.DELETED) || fullDelete == 1) {
|
||||
fileEntityService.deleteFromDisk(fileEntity);
|
||||
cloudStorageService.deleteFromStorageDisk(fileEntity);
|
||||
response = DeleteFileResponse.builder()
|
||||
.fileId(fileRequest.getFileId())
|
||||
.message("File deleted from disk")
|
||||
|
||||
@@ -8,6 +8,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import ru.soune.nocopy.entity.user.ProtectedFileCheck;
|
||||
import ru.soune.nocopy.repository.ProtectedFileCheckRepository;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
@@ -15,7 +16,10 @@ import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@@ -35,6 +39,7 @@ public class FileCleanupService {
|
||||
log.info("Starting cleanup of expired temporary files");
|
||||
|
||||
cleanupOldPeriods();
|
||||
cleanupTempFilesNightly();
|
||||
|
||||
Path tempDir = Paths.get(basePath, "temp");
|
||||
if (!Files.exists(tempDir)) {
|
||||
@@ -98,4 +103,50 @@ public class FileCleanupService {
|
||||
protectedFileCheckRepository.saveAll(checks);
|
||||
log.info("Cleaned up {} old protection usage records", checks.size());
|
||||
}
|
||||
|
||||
private void cleanupTempFilesNightly() {
|
||||
log.info("Starting nightly cleanup of temporary files");
|
||||
|
||||
String tempDir = System.getProperty("java.io.tmpdir");
|
||||
Path tempPath = Paths.get(tempDir);
|
||||
|
||||
if (!Files.exists(tempPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Instant cutoffTime = Instant.now().minus(24, ChronoUnit.HOURS); // 24 часа
|
||||
int deletedCount = 0;
|
||||
|
||||
try (Stream<Path> files = Files.list(tempPath)) {
|
||||
List<Path> tempFiles = files
|
||||
.filter(path -> {
|
||||
String filename = path.getFileName().toString();
|
||||
return filename.contains("_") &&
|
||||
!filename.endsWith(".tmp") &&
|
||||
Files.isRegularFile(path);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (Path filePath : tempFiles) {
|
||||
try {
|
||||
File file = filePath.toFile();
|
||||
Instant lastModified = Instant.ofEpochMilli(file.lastModified());
|
||||
|
||||
if (lastModified.isBefore(cutoffTime)) {
|
||||
boolean deleted = Files.deleteIfExists(filePath);
|
||||
if (deleted) {
|
||||
deletedCount++;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("Error deleting file: {}", filePath, e);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("Nightly cleanup deleted {} temporary files", deletedCount);
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to list temp directory", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ import ru.soune.nocopy.repository.SimilarImageProjection;
|
||||
import ru.soune.nocopy.repository.UserRepository;
|
||||
import ru.soune.nocopy.service.FileSimilarityService;
|
||||
import ru.soune.nocopy.service.ImageHashService;
|
||||
import ru.soune.nocopy.service.file.cloud.CloudStorageService;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -43,6 +43,8 @@ public class FileEntityService {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
|
||||
private final CloudStorageService cloudStorageService;
|
||||
|
||||
@Transactional(noRollbackFor = DuplicateImageException.class)
|
||||
public FileEntity createFromUploadSession(FileUploadSession session, String checksum) {
|
||||
log.info("Creating FileEntity for upload session: {}", session.getUploadId());
|
||||
@@ -56,7 +58,7 @@ public class FileEntityService {
|
||||
|
||||
Map<String, Long> imageHash = Map.of();
|
||||
|
||||
if (session.getFileType().startsWith("image")) {
|
||||
if (!session.getFileType().startsWith("video")) {
|
||||
imageHash = imageHashService.calculateHash(filePath);
|
||||
List<SimilarImageProjection> duplicatedByHash = fileSimilarityService.findDuplicatedByHash(
|
||||
imageHash.get("hi"), imageHash.get("low"));
|
||||
@@ -117,38 +119,6 @@ public class FileEntityService {
|
||||
return convertToResponse(fileEntity, version);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public FileEntityResponse getByFilePath(String filePath, int version) {
|
||||
FileEntity fileEntity = fileEntityRepository.findByFilePath(filePath)
|
||||
.orElseThrow(() -> new FileEntityNotFoundException("Path: " + filePath));
|
||||
|
||||
return convertToResponse(fileEntity, version);
|
||||
}
|
||||
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public FileResponse getAllUserFiles(Long userId, int version) {
|
||||
List<FileEntity> fileEntities = fileEntityRepository.findByUserIdAndStatus(
|
||||
userId, FileStatus.ACTIVE);
|
||||
|
||||
List<FileEntityResponse> files = fileEntities.stream()
|
||||
.map(file -> convertToResponse(file, version))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
long totalSize = fileEntities.stream()
|
||||
.mapToLong(FileEntity::getFileSize)
|
||||
.sum();
|
||||
|
||||
return FileResponse.builder()
|
||||
.files(files)
|
||||
.totalCount(files.size())
|
||||
.totalSize(totalSize)
|
||||
.formattedTotalSize(formatFileSize(totalSize))
|
||||
.page(1)
|
||||
.pageSize(files.size())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public FileResponse getUserFiles(Long userId, int page, int pageSize, int version) {
|
||||
User user = userRepository.findById(userId).orElseThrow();
|
||||
@@ -200,19 +170,6 @@ public class FileEntityService {
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public FileEntity markAsDeleted(FileEntity fileEntity) throws IOException {
|
||||
fileEntity.setStatus(FileStatus.DELETED);
|
||||
fileEntity.setUpdatedAt(LocalDateTime.now());
|
||||
fileEntity.setProtectionStatus(ProtectionStatus.NOT_PROTECTED);
|
||||
|
||||
Path path = Paths.get(fileEntity.getProtectedFilePath());
|
||||
Files.deleteIfExists(path);
|
||||
fileEntity.setProtectedFilePath("");
|
||||
|
||||
return fileEntityRepository.save(fileEntity);
|
||||
}
|
||||
|
||||
public void softDeleteFileWithHash(FileEntity fileEntity) throws IOException {
|
||||
if (fileEntity.getImageHash() != null) {
|
||||
fileEntity.setImageHash(null);
|
||||
@@ -229,16 +186,8 @@ public class FileEntityService {
|
||||
fileEntityRepository.save(fileEntity);
|
||||
}
|
||||
|
||||
public void deleteFromDisk(FileEntity fileEntity) throws IOException {
|
||||
Path path = Paths.get(fileEntity.getFilePath());
|
||||
|
||||
if (!Files.exists(path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Files.delete(path);
|
||||
|
||||
fileEntityRepository.delete(fileEntity);
|
||||
public void deleteFromDisk(String deleteFilePath) throws IOException {
|
||||
Files.deleteIfExists(Paths.get(deleteFilePath));
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
@@ -256,12 +205,11 @@ public class FileEntityService {
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void writeProtectedFile(String id, byte[] data, String fileExt) throws IOException {
|
||||
public FileEntity writeProtectedFile(String id, byte[] data, String fileExt) throws IOException {
|
||||
FileEntity fileEntity = fileEntityRepository.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("File not found: " + id));
|
||||
|
||||
String extension = determineFileExtension(fileExt, fileEntity);
|
||||
log.info("EXTENSION: {}", extension);
|
||||
Path protectedFilePath = prepareProtectedPath(fileEntity, extension);
|
||||
|
||||
if (Files.exists(protectedFilePath)) {
|
||||
@@ -276,14 +224,14 @@ public class FileEntityService {
|
||||
fileEntity.setFileExtension(extension);
|
||||
fileEntity.setProtectionStatus(ProtectionStatus.PROTECTED);
|
||||
|
||||
fileEntityRepository.save(fileEntity);
|
||||
return fileEntityRepository.save(fileEntity);
|
||||
}
|
||||
|
||||
public void clearTempFiles(long userId) throws IOException {
|
||||
public void clearTempFiles(long userId) {
|
||||
List<FileEntity> fileByUserIdAndStatus = fileEntityRepository.findFileByUserIdAndStatus(userId, FileStatus.TEMP);
|
||||
|
||||
for (FileEntity fileEntity : fileByUserIdAndStatus) {
|
||||
deleteFromDisk(fileEntity);
|
||||
cloudStorageService.deleteFromStorageDisk(fileEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,24 +243,6 @@ public class FileEntityService {
|
||||
return fileEntityRepository.findFileIdByFilePath(filePath);
|
||||
}
|
||||
|
||||
public File getFileById(String id) {
|
||||
try {
|
||||
FileEntity fileEntity = fileEntityRepository.findById(id).orElseThrow(() ->
|
||||
new RuntimeException("File not found: " + id));
|
||||
|
||||
File file = new File(fileEntity.getFilePath());
|
||||
|
||||
if (!file.exists()) {
|
||||
throw new RuntimeException("File not found on disk: " + fileEntity.getFilePath());
|
||||
}
|
||||
|
||||
return file;
|
||||
} catch (Exception e) {
|
||||
log.error("Error getting file: {}", id, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Path prepareProtectedPath(FileEntity fileEntity, String extension) throws IOException {
|
||||
Path originalPath = Paths.get(fileEntity.getFilePath());
|
||||
|
||||
@@ -349,6 +279,18 @@ public class FileEntityService {
|
||||
fileEntityRepository.save(fileEntity);
|
||||
}
|
||||
|
||||
public String formatFileSize(long size) {
|
||||
if (size < 1024) {
|
||||
return size + " B";
|
||||
} else if (size < 1024 * 1024) {
|
||||
return String.format("%.1f KB", size / 1024.0);
|
||||
} else if (size < 1024 * 1024 * 1024) {
|
||||
return String.format("%.1f MB", size / (1024.0 * 1024.0));
|
||||
} else {
|
||||
return String.format("%.1f GB", size / (1024.0 * 1024.0 * 1024.0));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkFileExistsOnDisk(String filePath) {
|
||||
try {
|
||||
return Files.exists(Paths.get(filePath));
|
||||
@@ -401,16 +343,4 @@ public class FileEntityService {
|
||||
.protectedFilePath(fileEntity.getProtectedFilePath())
|
||||
.build();
|
||||
}
|
||||
|
||||
public String formatFileSize(long size) {
|
||||
if (size < 1024) {
|
||||
return size + " B";
|
||||
} else if (size < 1024 * 1024) {
|
||||
return String.format("%.1f KB", size / 1024.0);
|
||||
} else if (size < 1024 * 1024 * 1024) {
|
||||
return String.format("%.1f MB", size / (1024.0 * 1024.0));
|
||||
} else {
|
||||
return String.format("%.1f GB", size / (1024.0 * 1024.0 * 1024.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package ru.soune.nocopy.service.file;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import ru.soune.nocopy.dto.file.UploadProgressResponse;
|
||||
import ru.soune.nocopy.entity.file.FileEntity;
|
||||
import ru.soune.nocopy.entity.file.FileStatus;
|
||||
import ru.soune.nocopy.entity.file.FileUploadSession;
|
||||
|
||||
@@ -19,5 +20,5 @@ public interface FileUploadService {
|
||||
|
||||
UploadProgressResponse getUploadProgress(String uploadId);
|
||||
|
||||
void completeFileProcessingAsync(FileUploadSession session, FileStatus status);
|
||||
FileEntity completeFileProcessingAsync(FileUploadSession session, FileStatus status);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
package ru.soune.nocopy.service.file.cloud;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.soune.nocopy.entity.file.FileEntity;
|
||||
import ru.soune.nocopy.repository.FileEntityRepository;
|
||||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentials;
|
||||
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
|
||||
import software.amazon.awssdk.core.sync.RequestBody;
|
||||
import software.amazon.awssdk.http.apache.ApacheHttpClient;
|
||||
import software.amazon.awssdk.regions.Region;
|
||||
import software.amazon.awssdk.services.s3.S3Client;
|
||||
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
|
||||
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class CloudStorageService {
|
||||
|
||||
private final FileEntityRepository fileEntityRepository;
|
||||
|
||||
@Value("${yandex.cloud.key}")
|
||||
private String key;
|
||||
|
||||
@Value("${yandex.cloud.secret-key}")
|
||||
private String secretKey;
|
||||
|
||||
@Value("${yandex.cloud.region}")
|
||||
private String region;
|
||||
|
||||
@Value("${yandex.cloud.s3-endpoint}")
|
||||
private String s3endpoint;
|
||||
|
||||
@Value("${yandex.cloud.bucket}")
|
||||
private String bucket;
|
||||
|
||||
public void saveFilesToStorage(String mimeType, String extension, String sendToCloudFilePath) {
|
||||
AwsCredentials credentials = AwsBasicCredentials.create(key, secretKey);
|
||||
|
||||
S3Client s3Client = S3Client.builder()
|
||||
.httpClient(ApacheHttpClient.create())
|
||||
.region(Region.of(region))
|
||||
.endpointOverride(URI.create(s3endpoint))
|
||||
.credentialsProvider(StaticCredentialsProvider.create(credentials))
|
||||
.build();
|
||||
|
||||
String mimeTypeFromFile = mimeType.equals("document") ? "application" : mimeType;
|
||||
String contentType = mimeTypeFromFile + "/" + extension;
|
||||
String filePath = "files" + sendToCloudFilePath;
|
||||
|
||||
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
|
||||
.bucket(bucket)
|
||||
.key(filePath)
|
||||
.contentType(contentType)
|
||||
.build();
|
||||
|
||||
try {
|
||||
s3Client.putObject(putObjectRequest,
|
||||
RequestBody.fromBytes(Files.readAllBytes(Paths.get(sendToCloudFilePath))));
|
||||
Files.deleteIfExists(Paths.get(sendToCloudFilePath));
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to read file: {}", filePath, e);
|
||||
}
|
||||
}
|
||||
|
||||
public File readFileFromStorageByPath(String filePath) throws IOException {
|
||||
GetObjectRequest objectRequest = GetObjectRequest.builder()
|
||||
.bucket(bucket)
|
||||
.key("files" + filePath)
|
||||
.build();
|
||||
AwsCredentials credentials = AwsBasicCredentials.create(key, secretKey);
|
||||
|
||||
S3Client s3Client = S3Client.builder()
|
||||
.httpClient(ApacheHttpClient.create())
|
||||
.region(Region.of(region))
|
||||
.endpointOverride(URI.create(s3endpoint))
|
||||
.credentialsProvider(StaticCredentialsProvider.create(credentials))
|
||||
.build();
|
||||
|
||||
String tempDir = System.getProperty("java.io.tmpdir");
|
||||
String fileName = filePath.replace("/", "_");
|
||||
File tempFile = new File(tempDir, fileName);
|
||||
|
||||
s3Client.getObject(objectRequest, tempFile.toPath());
|
||||
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
public void deleteFileFromStorage(String filePath) {
|
||||
if (filePath == null || filePath.isEmpty()) {
|
||||
log.warn("Attempted to delete file with empty path");
|
||||
return;
|
||||
}
|
||||
|
||||
AwsCredentials credentials = AwsBasicCredentials.create(key, secretKey);
|
||||
|
||||
S3Client s3Client = S3Client.builder()
|
||||
.httpClient(ApacheHttpClient.create())
|
||||
.region(Region.of(region))
|
||||
.endpointOverride(URI.create(s3endpoint))
|
||||
.credentialsProvider(StaticCredentialsProvider.create(credentials))
|
||||
.build();
|
||||
|
||||
String cloudKey = "files" + filePath;
|
||||
|
||||
try {
|
||||
try {
|
||||
s3Client.headObject(builder -> builder.bucket(bucket).key(cloudKey).build());
|
||||
} catch (Exception e) {
|
||||
log.warn("File does not exist in cloud storage: {}", cloudKey);
|
||||
return;
|
||||
}
|
||||
|
||||
s3Client.deleteObject(builder -> builder.bucket(bucket).key(cloudKey).build());
|
||||
|
||||
log.info("File successfully deleted from cloud storage: {}", cloudKey);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to delete file from cloud storage: {}", cloudKey, e);
|
||||
throw new RuntimeException("Failed to delete file from cloud storage: " + cloudKey, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteFromStorageDisk(FileEntity fileEntity) {
|
||||
deleteFileFromStorage(fileEntity.getFilePath());
|
||||
|
||||
if (fileEntity.getProtectedFilePath() != null) {
|
||||
deleteFileFromStorage(fileEntity.getProtectedFilePath());
|
||||
}
|
||||
|
||||
fileEntityRepository.delete(fileEntity);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package ru.soune.nocopy.service.file.impl;
|
||||
import com.vrt.NoCopyFileService;
|
||||
import com.vrt.fileprotection.FileProtector;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -24,6 +25,7 @@ import ru.soune.nocopy.service.FileSimilarityService;
|
||||
import ru.soune.nocopy.service.ImageHashService;
|
||||
import ru.soune.nocopy.service.file.FileEntityService;
|
||||
import ru.soune.nocopy.service.file.FileUploadService;
|
||||
import ru.soune.nocopy.service.file.cloud.CloudStorageService;
|
||||
import ru.soune.nocopy.service.tariff.TariffConstants;
|
||||
import ru.soune.nocopy.service.tariff.TariffInfoService;
|
||||
import ru.soune.nocopy.util.FileUtil;
|
||||
@@ -83,6 +85,8 @@ public class FileUploadServiceImpl implements FileUploadService {
|
||||
|
||||
private final TariffInfoService tariffInfoService;
|
||||
|
||||
private final CloudStorageService cloudStorageService;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
try {
|
||||
@@ -177,7 +181,6 @@ public class FileUploadServiceImpl implements FileUploadService {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public UploadProgressResponse uploadChunk(String uploadId, Integer chunkNumber,
|
||||
MultipartFile chunkFile, Integer findSimilar) {
|
||||
FileUploadSession session = sessionRepository.findById(uploadId)
|
||||
@@ -219,7 +222,8 @@ public class FileUploadServiceImpl implements FileUploadService {
|
||||
@Async("fileUploadTaskExecutor")
|
||||
@Transactional
|
||||
@Override
|
||||
public void completeFileProcessingAsync(FileUploadSession session, FileStatus status) {
|
||||
public FileEntity completeFileProcessingAsync(FileUploadSession session, FileStatus status) {
|
||||
FileEntity saved = null;
|
||||
try {
|
||||
Path filePath = Paths.get(session.getFilePath());
|
||||
String checksum = calculateChecksum(filePath);
|
||||
@@ -238,7 +242,7 @@ public class FileUploadServiceImpl implements FileUploadService {
|
||||
.status(status)
|
||||
.build();
|
||||
|
||||
FileEntity saved = fileEntityRepository.save(fileEntity);
|
||||
saved = fileEntityRepository.save(fileEntity);
|
||||
|
||||
if (session.getFileType().equals("image")) {
|
||||
Map<String, Long> hash = imageHashService.calculateHash(filePath);
|
||||
@@ -248,18 +252,13 @@ public class FileUploadServiceImpl implements FileUploadService {
|
||||
|
||||
cleanupSessionFiles(session);
|
||||
|
||||
if (status != FileStatus.TEMP) {
|
||||
FileProtector.FileInfo fileInfo = fileUtil.createFileInfo(fileEntity);
|
||||
|
||||
noCopyFileService.addFile(fileInfo);
|
||||
}
|
||||
|
||||
log.info("File processing completed for session: {}", session.getUploadId());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to complete file processing for session {}: {}",
|
||||
session.getUploadId(), e.getMessage(), e);
|
||||
}
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
private void validateSession(FileUploadSession session) {
|
||||
@@ -291,6 +290,9 @@ public class FileUploadServiceImpl implements FileUploadService {
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private EntityManager entityManager;
|
||||
|
||||
private UploadProgressResponse processChunk(FileUploadSession session, Integer chunkNumber, MultipartFile chunkFile,
|
||||
Integer findSimilar) {
|
||||
String chunkPath = null;
|
||||
@@ -331,8 +333,20 @@ public class FileUploadServiceImpl implements FileUploadService {
|
||||
fileEntityService.clearTempFiles(session.getUserId());
|
||||
}
|
||||
|
||||
completeFileProcessingAsync(session, status);
|
||||
FileEntity fileEntity = completeFileProcessingAsync(session, status);
|
||||
|
||||
fileEntityRepository.flush();
|
||||
|
||||
entityManager.clear();
|
||||
|
||||
cloudStorageService.saveFilesToStorage(fileEntity.getMimeType(), fileEntity.getFileExtension(),
|
||||
fileEntity.getFilePath());
|
||||
|
||||
if (status != FileStatus.TEMP) {
|
||||
FileProtector.FileInfo fileInfo = fileUtil.createFileInfo(fileEntity);
|
||||
|
||||
noCopyFileService.addFile(fileInfo);
|
||||
}
|
||||
|
||||
if (status != FileStatus.TEMP) {
|
||||
String fileType = session.getFileType();
|
||||
|
||||
@@ -7,7 +7,10 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.springframework.stereotype.Component;
|
||||
import ru.soune.nocopy.entity.file.FileEntity;
|
||||
import ru.soune.nocopy.repository.FileEntityRepository;
|
||||
import ru.soune.nocopy.service.file.FileEntityService;
|
||||
import ru.soune.nocopy.service.file.cloud.CloudStorageService;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -24,27 +27,55 @@ public class ProtectionFileProviderImpl implements FileProtector.FileProvider {
|
||||
|
||||
private final FileEntityService fileEntityService;
|
||||
|
||||
private final CloudStorageService cloudStorageService;
|
||||
|
||||
private final FileEntityRepository fileEntityRepository;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public File getImageFile(@NotNull String id) {
|
||||
return fileEntityService.getFileById(id);
|
||||
FileEntity fileEntity = fileEntityRepository.findById(id).orElseThrow(() ->
|
||||
new RuntimeException("File not found: " + id));
|
||||
try {
|
||||
return cloudStorageService.readFileFromStorageByPath(fileEntity.getFilePath());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public File getVideoFile(@NotNull String id) {
|
||||
return fileEntityService.getFileById(id);
|
||||
FileEntity fileEntity = fileEntityRepository.findById(id).orElseThrow(() ->
|
||||
new RuntimeException("File not found: " + id));
|
||||
try {
|
||||
return cloudStorageService.readFileFromStorageByPath(fileEntity.getFilePath());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public File getAudioFile(@NotNull String id) {
|
||||
return fileEntityService.getFileById(id);
|
||||
FileEntity fileEntity = fileEntityRepository.findById(id).orElseThrow(() ->
|
||||
new RuntimeException("File not found: " + id));
|
||||
try {
|
||||
return cloudStorageService.readFileFromStorageByPath(fileEntity.getFilePath());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable File getDocument(@NotNull String id) {
|
||||
return fileEntityService.getFileById(id);
|
||||
FileEntity fileEntity = fileEntityRepository.findById(id).orElseThrow(() ->
|
||||
new RuntimeException("File not found: " + id));
|
||||
try {
|
||||
return cloudStorageService.readFileFromStorageByPath(fileEntity.getFilePath());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,7 +110,11 @@ public class ProtectionFileProviderImpl implements FileProtector.FileProvider {
|
||||
@Override
|
||||
public com.vrt.fileprotection.OperationResult writeAudioFile(@NotNull String id, @NotNull byte[] data, @Nullable String fileExt) {
|
||||
try {
|
||||
fileEntityService.writeProtectedFile(id, data, fileExt);
|
||||
FileEntity fileEntity = fileEntityService.writeProtectedFile(id, data, fileExt);
|
||||
|
||||
cloudStorageService.saveFilesToStorage(fileEntity.getMimeType(), fileEntity.getFileExtension(),
|
||||
fileEntity.getProtectedFilePath());
|
||||
|
||||
return OperationResult.Companion.success();
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to create protected file: {}", id, e);
|
||||
@@ -91,7 +126,11 @@ public class ProtectionFileProviderImpl implements FileProtector.FileProvider {
|
||||
@Override
|
||||
public com.vrt.fileprotection.OperationResult writeVideoFile(@NotNull String id, @NotNull byte[] data, @Nullable String fileExt) {
|
||||
try {
|
||||
fileEntityService.writeProtectedFile(id, data, fileExt);
|
||||
FileEntity fileEntity = fileEntityService.writeProtectedFile(id, data, fileExt);
|
||||
|
||||
cloudStorageService.saveFilesToStorage(fileEntity.getMimeType(), fileEntity.getFileExtension(),
|
||||
fileEntity.getProtectedFilePath());
|
||||
|
||||
return OperationResult.Companion.success();
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to create protected file: {}", id, e);
|
||||
@@ -103,7 +142,11 @@ public class ProtectionFileProviderImpl implements FileProtector.FileProvider {
|
||||
@Override
|
||||
public com.vrt.fileprotection.OperationResult writeImageFile(@NotNull String id, @NotNull byte[] data, @Nullable String fileExt) {
|
||||
try {
|
||||
fileEntityService.writeProtectedFile(id, data, fileExt);
|
||||
FileEntity fileEntity = fileEntityService.writeProtectedFile(id, data, fileExt);
|
||||
|
||||
cloudStorageService.saveFilesToStorage(fileEntity.getMimeType(), fileEntity.getFileExtension(),
|
||||
fileEntity.getProtectedFilePath());
|
||||
|
||||
return OperationResult.Companion.success();
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to create protected file: {}", id, e);
|
||||
@@ -114,7 +157,10 @@ public class ProtectionFileProviderImpl implements FileProtector.FileProvider {
|
||||
@Override
|
||||
public @NotNull OperationResult writeDocumentFile(@NotNull String id, @NotNull byte[] data) {
|
||||
try {
|
||||
fileEntityService.writeProtectedFile(id, data, null);
|
||||
FileEntity fileEntity = fileEntityService.writeProtectedFile(id, data, null);
|
||||
|
||||
cloudStorageService.saveFilesToStorage(fileEntity.getMimeType(), fileEntity.getFileExtension(),
|
||||
fileEntity.getProtectedFilePath());
|
||||
return OperationResult.Companion.success();
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to create protected file: {}", id, e);
|
||||
|
||||
@@ -100,6 +100,13 @@ yandex:
|
||||
api-key: ${YANDEX_API_KEY:AQVNyaVaUmgUb1GMCtf5zSEqFxy0woXrcMOOB43q}
|
||||
folder-id: ${YANDEX_FOLDER_ID:b1gokpdbm6qfpsou8pcd}
|
||||
search-url: ${YANDEX_SEARCH_URL:https://searchapi.api.cloud.yandex.net/v2/image/search_by_image}
|
||||
cloud:
|
||||
key: "YCAJEmHZcWxzf4oGWvETG3mms"
|
||||
secret-key: "YCPAPZ32XC-LWb8xE0b_xTcBD8NZUCHpSOfu8LUG"
|
||||
region: "ru-central1"
|
||||
s3-endpoint: "https://storage.yandexcloud.net"
|
||||
bucket: "no-copy-storage"
|
||||
|
||||
|
||||
searchapi:
|
||||
api-key: ${SEARCHAPI_API_KEY:rdwNzCr2eybYVbU1KrGTJAgm}
|
||||
|
||||
Reference in New Issue
Block a user