diff --git a/src/main/java/ru/soune/nocopy/repository/FileEntityRepository.java b/src/main/java/ru/soune/nocopy/repository/FileEntityRepository.java index 2fd8655..9e098db 100644 --- a/src/main/java/ru/soune/nocopy/repository/FileEntityRepository.java +++ b/src/main/java/ru/soune/nocopy/repository/FileEntityRepository.java @@ -52,4 +52,7 @@ public interface FileEntityRepository extends JpaRepository String findFileIdByFilePath(@Param("filePath") String filePath); List findFileByUserIdAndStatus(Long userId, FileStatus status); + + @Query("SELECT f FROM FileEntity f WHERE f.userId = :userId AND f.mimeType = :mimeType") + List findByUserIdAndMimeType(Long userId, String mimeType); } diff --git a/src/main/java/ru/soune/nocopy/service/FileSimilarityService.java b/src/main/java/ru/soune/nocopy/service/FileSimilarityService.java index b4be9e9..5b4c8f5 100644 --- a/src/main/java/ru/soune/nocopy/service/FileSimilarityService.java +++ b/src/main/java/ru/soune/nocopy/service/FileSimilarityService.java @@ -11,13 +11,14 @@ import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import ru.soune.nocopy.dto.file.SimilarFileDTO; import ru.soune.nocopy.dto.file.SimilarityFilter; +import ru.soune.nocopy.entity.file.FileEntity; import ru.soune.nocopy.entity.file.ImageHashEntity; -import ru.soune.nocopy.repository.AuthTokenRepository; -import ru.soune.nocopy.repository.ImageHashRepository; -import ru.soune.nocopy.repository.ImageSimilarityRepository; -import ru.soune.nocopy.repository.SimilarImageProjection; +import ru.soune.nocopy.repository.*; import ru.soune.nocopy.util.FileUtil; +import java.io.FileInputStream; +import java.io.InputStream; +import java.security.MessageDigest; import java.util.*; import java.util.stream.Collectors; @@ -137,25 +138,43 @@ public class FileSimilarityService { return duplicates; } - public Page findSimilarFiles(String fileId, SimilarityFilter filter, Pageable pageable, String authToken) { - var imageHashEntity = hashRepository.findById(fileId) - .orElseThrow(() -> new RuntimeException("Hash not found")); + private final FileEntityRepository fileEntityRepository; - Long hash64Hi = imageHashEntity.getHash64Hi(); - Long hash64Lo = imageHashEntity.getHash64Lo(); + public Page findSimilarFiles(String fileId, SimilarityFilter filter, Pageable pageable, + String authToken) throws Exception { + FileEntity fileEntity = fileEntityRepository.findByFileId(fileId); + String mimeType = fileEntity.getMimeType(); + List allResults = new ArrayList<>(); - List candidates = authToken.equals("all") ? repository.findCandidates(fileId): - repository.findCandidatesFromUserFiles(fileId, authTokenRepository.findUserIdByToken(authToken)); + if (mimeType.equals("image")) { + var imageHashEntity = hashRepository.findById(fileId) + .orElseThrow(() -> new RuntimeException("Hash not found")); - List similarityLevels = (filter != null && filter.getSimilarityLevels() != null) - ? filter.getSimilarityLevels() - : List.of("DUPLICATE", "SIMILAR", "DIFFERENT"); + Long hash64Hi = imageHashEntity.getHash64Hi(); + Long hash64Lo = imageHashEntity.getHash64Lo(); + List candidates = authToken.equals("all") ? repository.findCandidates(fileId): + repository.findCandidatesFromUserFiles(fileId, authTokenRepository.findUserIdByToken(authToken)); - List allResults = candidates.stream() - .map(c -> createSimilarFileResponse(c, hash64Hi, hash64Lo)) - .filter(response -> similarityLevels.contains(response.getSimilarityLevel())) - .sorted(Comparator.comparingInt(SimilarFileDTO::getHammingDistance)) - .collect(Collectors.toList()); + List similarityLevels = (filter != null && filter.getSimilarityLevels() != null) + ? filter.getSimilarityLevels() + : List.of("DUPLICATE", "SIMILAR", "DIFFERENT"); + + allResults = candidates.stream() + .map(c -> createSimilarFileResponse(c, hash64Hi, hash64Lo)) + .filter(response -> similarityLevels.contains(response.getSimilarityLevel())) + .sorted(Comparator.comparingInt(SimilarFileDTO::getHammingDistance)) + .collect(Collectors.toList()); + } else { + String hash = calculateFileHash(fileEntity); + Long userId = fileEntity.getUserId(); + List fileEntityList = fileEntityRepository.findByUserIdAndMimeType(userId, mimeType); + + for (FileEntity file : fileEntityList) { + if (calculateFileHash(file).equals(hash)) { + allResults.add(buildDTO(file)); + } + } + } int total = allResults.size(); int page = (pageable != null) ? pageable.getPageNumber() : 0; @@ -169,6 +188,35 @@ public class FileSimilarityService { return new PageImpl<>(pageContent, pageable, total); } + + private String calculateFileHash(FileEntity fileEntity) throws Exception { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + +// if (fileEntity.getStatus() == FileStatus.CLOUD) { +// File tempFile = cloudStorageService.readFileFromStorageByPath(fileEntity.getFilePath()); +// try (InputStream is = new FileInputStream(tempFile)) { +// byte[] buffer = new byte[8192]; +// int read; +// while ((read = is.read(buffer)) > 0) { +// digest.update(buffer, 0, read); +// } +// } finally { +// tempFile.delete(); +// } +// } else { + try (InputStream is = new FileInputStream(fileEntity.getFilePath())) { + byte[] buffer = new byte[8192]; + int read; + while ((read = is.read(buffer)) > 0) { + digest.update(buffer, 0, read); + } +// } + } + + byte[] hashBytes = digest.digest(); + return Base64.getEncoder().encodeToString(hashBytes); + } + private SimilarFileDTO createSimilarFileResponse(SimilarImageProjection similarImageProjection, Long hash64Hi, Long hash64Lo) { Long imageProjectionHash64Hi = similarImageProjection.getHash64Hi(); @@ -198,4 +246,17 @@ public class FileSimilarityService { .url(baseUrl + "/api/files/protected/" + similarImageProjection.getId()) .build(); } + + private SimilarFileDTO buildDTO(FileEntity fileEntity) { + return SimilarFileDTO.builder() + .fileId(fileEntity.getId()) + .ownerId(fileEntity.getUserId()) + .originalFileName(fileEntity.getOriginalFileName()) + .fileSize(fileEntity.getFileSize()) + .supportId(Long.valueOf(fileEntity.getSupportId())) + .uploadDate(fileEntity.getCreatedAt()) + .status(fileEntity.getProtectionStatus()) + .url(baseUrl + "/api/files/protected/" + fileEntity.getId()) + .build(); + } }