NCBACK-25 add protection for audio and use hash method from library
Test Workflow / test (push) Successful in 2s
Test Workflow / test (push) Successful in 2s
This commit is contained in:
@@ -2,6 +2,7 @@ package ru.soune.nocopy.controller;
|
||||
|
||||
import com.vrt.NoCopyFileService;
|
||||
import com.vrt.fileprotection.FileProtector;
|
||||
import com.vrt.fileprotection.NoCopyCheckResult;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.io.Resource;
|
||||
@@ -38,6 +39,7 @@ import ru.soune.nocopy.service.file.FileEntityService;
|
||||
import ru.soune.nocopy.service.file.FileUploadService;
|
||||
import ru.soune.nocopy.util.FileUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -380,6 +382,25 @@ public class ApiController {
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@GetMapping("/check/{fileId}/{type}")
|
||||
public ResponseEntity<?> check(@PathVariable(required = false) String fileId,
|
||||
@PathVariable(required = false) String type) {
|
||||
Optional<FileEntity> optionalFileEntity = fileEntityRepository.findById(fileId);
|
||||
if (!optionalFileEntity.isPresent()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
FileEntity fileEntity = optionalFileEntity.get();
|
||||
|
||||
Path path = Paths.get(fileEntity.getFilePath());
|
||||
File file = path.toFile();
|
||||
|
||||
NoCopyCheckResult noCopyCheckResult = noCopyFileService.checkFile(file,
|
||||
FileProtector.Type.valueOf(type.toUpperCase()));
|
||||
|
||||
return ResponseEntity.ok().body(noCopyCheckResult);
|
||||
}
|
||||
|
||||
private ResponseEntity<BaseResponse> checkForDuplicates(String uploadId) throws IOException {
|
||||
Optional<FileEntity> uploadedFile = fileEntityRepository.findByUploadSessionId(uploadId);
|
||||
|
||||
|
||||
@@ -26,10 +26,10 @@ public class ImageHashEntity {
|
||||
private FileEntity file;
|
||||
|
||||
@Column(name = "hash64_hi")
|
||||
private Integer hash64Hi;
|
||||
private Long hash64Hi;
|
||||
|
||||
@Column(name = "hash64_lo")
|
||||
private Integer hash64Lo;
|
||||
private Long hash64Lo;
|
||||
|
||||
@Column(name = "hash_algorithm", nullable = false)
|
||||
private String hashAlgorithm;
|
||||
|
||||
@@ -10,5 +10,5 @@ import java.util.List;
|
||||
public interface ImageHashRepository extends JpaRepository<ImageHashEntity, String> {
|
||||
void deleteByFileId(String fileId);
|
||||
List<ImageHashEntity> findByFileId(String fileId);
|
||||
ImageHashEntity findByHash64HiAndHash64Lo(Integer hash64Hi, Integer hash64Lo);
|
||||
ImageHashEntity findByHash64HiAndHash64Lo(Long hash64Hi, Long hash64Lo);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ public interface ImageSimilarityRepository
|
||||
""",
|
||||
nativeQuery = true)
|
||||
List<SimilarImageProjection> findExactDuplicates(
|
||||
@Param("hash64Hi") Integer hash64_hi,
|
||||
@Param("hash64Lo") Integer hash64_lo);
|
||||
@Param("hash64Hi") Long hash64_hi,
|
||||
@Param("hash64Lo") Long hash64_lo);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package ru.soune.nocopy.repository;
|
||||
|
||||
public interface SimilarImageProjection {
|
||||
Integer getHash64Hi();
|
||||
Integer getHash64Lo();
|
||||
Long getHash64Hi();
|
||||
Long getHash64Lo();
|
||||
String getFileId();
|
||||
Long getUserId();
|
||||
String getOriginalFileName();
|
||||
|
||||
@@ -32,15 +32,15 @@ public class FileSimilarityService {
|
||||
var imageHashEntity = hashRepository.findById(fileId)
|
||||
.orElseThrow(() -> new RuntimeException("Hash not found"));
|
||||
|
||||
Integer hash64Hi = imageHashEntity.getHash64Hi();
|
||||
Integer hash64Lo = imageHashEntity.getHash64Lo();
|
||||
Long hash64Hi = imageHashEntity.getHash64Hi();
|
||||
Long hash64Lo = imageHashEntity.getHash64Lo();
|
||||
|
||||
List<SimilarImageProjection> candidates = repository.findCandidates(fileId);
|
||||
|
||||
return candidates.stream()
|
||||
.map(c -> {
|
||||
Integer cHi = c.getHash64Hi();
|
||||
Integer cLo = c.getHash64Lo();
|
||||
Long cHi = c.getHash64Hi();
|
||||
Long cLo = c.getHash64Lo();
|
||||
|
||||
int hamming = fileUtil.hamming64(hash64Hi, hash64Lo, cHi, cLo);
|
||||
|
||||
@@ -73,13 +73,13 @@ public class FileSimilarityService {
|
||||
var imageHashEntity = hashRepository.findById(fileId)
|
||||
.orElseThrow(() -> new RuntimeException("Hash not found"));
|
||||
|
||||
Integer hash64Hi = imageHashEntity.getHash64Hi();
|
||||
Integer hash64Lo = imageHashEntity.getHash64Lo();
|
||||
Long hash64Hi = imageHashEntity.getHash64Hi();
|
||||
Long hash64Lo = imageHashEntity.getHash64Lo();
|
||||
|
||||
return candidates.stream()
|
||||
.map(c -> {
|
||||
Integer cHi = c.getHash64Hi();
|
||||
Integer cLo = c.getHash64Lo();
|
||||
Long cHi = c.getHash64Hi();
|
||||
Long cLo = c.getHash64Lo();
|
||||
|
||||
int hamming = fileUtil.hamming64(hash64Hi, hash64Lo, cHi, cLo);
|
||||
|
||||
@@ -112,7 +112,7 @@ public class FileSimilarityService {
|
||||
.toList();
|
||||
}
|
||||
|
||||
public List<SimilarImageProjection> findDuplicatedByHash(Integer hash64Hi, Integer hash64Lo) {
|
||||
public List<SimilarImageProjection> findDuplicatedByHash(Long hash64Hi, Long hash64Lo) {
|
||||
List<SimilarImageProjection> duplicates = repository.findExactDuplicates(hash64Hi,hash64Lo);
|
||||
|
||||
if (duplicates.isEmpty()) {
|
||||
@@ -126,8 +126,8 @@ public class FileSimilarityService {
|
||||
var imageHashEntity = hashRepository.findById(fileId)
|
||||
.orElseThrow(() -> new RuntimeException("Hash not found"));
|
||||
|
||||
Integer hash64Hi = imageHashEntity.getHash64Hi();
|
||||
Integer hash64Lo = imageHashEntity.getHash64Lo();
|
||||
Long hash64Hi = imageHashEntity.getHash64Hi();
|
||||
Long hash64Lo = imageHashEntity.getHash64Lo();
|
||||
|
||||
List<SimilarImageProjection> candidates = repository.findCandidates(fileId);
|
||||
|
||||
@@ -154,9 +154,9 @@ public class FileSimilarityService {
|
||||
}
|
||||
|
||||
private SimilarFileDTO createSimilarFileResponse(SimilarImageProjection similarImageProjection,
|
||||
Integer hash64Hi, Integer hash64Lo) {
|
||||
Integer imageProjectionHash64Hi = similarImageProjection.getHash64Hi();
|
||||
Integer similarImageProjectionHash64Lo = similarImageProjection.getHash64Lo();
|
||||
Long hash64Hi, Long hash64Lo) {
|
||||
Long imageProjectionHash64Hi = similarImageProjection.getHash64Hi();
|
||||
Long similarImageProjectionHash64Lo = similarImageProjection.getHash64Lo();
|
||||
|
||||
int hamming = fileUtil.hamming64(hash64Hi, hash64Lo, imageProjectionHash64Hi, similarImageProjectionHash64Lo);
|
||||
|
||||
|
||||
@@ -22,19 +22,19 @@ public class ImageHashService {
|
||||
|
||||
private final ImageHashRepository repository;
|
||||
|
||||
public Map<String, Integer> calculateHash(Path imagePath) throws IOException {
|
||||
public Map<String, Long> calculateHash(Path imagePath) throws IOException {
|
||||
File file = imagePath.toFile();
|
||||
PHash pHash = PerceptualHashHelper.INSTANCE.generateDCTPerceptualHash(file);
|
||||
Long firstPart = pHash.getFirstPart();
|
||||
Long secondPart = pHash.getSecondPart();
|
||||
Map<String, Integer> hash = Map.of(
|
||||
"hi", Math.toIntExact(firstPart),
|
||||
"low", Math.toIntExact(secondPart));
|
||||
Map<String, Long> hash = Map.of(
|
||||
"hi", firstPart,
|
||||
"low", secondPart);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
public void create(FileEntity file, Map<String, Integer> stringIntegerMap) {
|
||||
public void create(FileEntity file, Map<String, Long> stringIntegerMap) {
|
||||
ImageHashEntity entity = ImageHashEntity.builder()
|
||||
.file(file)
|
||||
.hash64Hi(stringIntegerMap.get("hi"))
|
||||
|
||||
@@ -49,7 +49,7 @@ public class FileEntityService {
|
||||
throw new IOException("File not found on disk: " + filePath);
|
||||
}
|
||||
|
||||
Map<String, Integer> imageHash = Map.of();
|
||||
Map<String, Long> imageHash = Map.of();
|
||||
|
||||
if (session.getFileType().startsWith("image")) {
|
||||
imageHash = imageHashService.calculateHash(filePath);
|
||||
|
||||
@@ -283,12 +283,16 @@ public class FileUploadServiceImpl implements FileUploadService {
|
||||
|
||||
FileEntity saved = fileEntityRepository.save(fileEntity);
|
||||
|
||||
Map<String, Integer> hash = imageHashService.calculateHash(filePath);
|
||||
if (session.getFileType().equals("image")) {
|
||||
Map<String, Long> hash = imageHashService.calculateHash(filePath);
|
||||
|
||||
imageHashService.create(saved, hash);
|
||||
imageHashService.create(saved, hash);
|
||||
}
|
||||
|
||||
cleanupSessionFiles(session);
|
||||
|
||||
noCopyFileService.addFile(fileUtil.createFileInfo(fileEntity));
|
||||
|
||||
log.info("File processing completed for session: {}", session.getUploadId());
|
||||
|
||||
} catch (Exception e) {
|
||||
@@ -380,7 +384,8 @@ public class FileUploadServiceImpl implements FileUploadService {
|
||||
private void checkForDuplicatesSynchronously(String filePath)
|
||||
throws IOException {
|
||||
Path path = Paths.get(filePath);
|
||||
Map<String, Integer> hash = imageHashService.calculateHash(path);
|
||||
Map<String, Long> hash = imageHashService.calculateHash(path);
|
||||
|
||||
List<SimilarImageProjection> duplicates = fileSimilarityService.findDuplicatedByHash(
|
||||
hash.get("hi"), hash.get("low"));
|
||||
|
||||
|
||||
@@ -44,8 +44,8 @@ public class ImageLocalSearchImpl implements ImageLocalSearch {
|
||||
@Override
|
||||
public @Nullable FileProtector.FileInfo findByPHash(@NotNull PHash pHash) {
|
||||
ImageHashEntity imageHashEntity =
|
||||
imageHashRepository.findByHash64HiAndHash64Lo(Math.toIntExact(pHash.getFirstPart()),
|
||||
Math.toIntExact(pHash.getSecondPart()));
|
||||
imageHashRepository.findByHash64HiAndHash64Lo(pHash.getFirstPart(),
|
||||
pHash.getSecondPart());
|
||||
FileEntity file = fileEntityRepository.findByFileId(imageHashEntity.getFileId());
|
||||
|
||||
return new FileProtector.FileInfo(FileProtector.Type.IMAGE, imageHashEntity.getFileId(),
|
||||
|
||||
@@ -10,12 +10,18 @@ import org.springframework.stereotype.Component;
|
||||
import ru.soune.nocopy.service.file.FileEntityService;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class ProtectionFileProviderImpl implements FileProtector.FileProvider {
|
||||
|
||||
private static final Path SIGNATURE_FILE_PATH = Paths.get("/data/uploads/signature");
|
||||
|
||||
private final FileEntityService fileEntityService;
|
||||
|
||||
@Nullable
|
||||
@@ -38,9 +44,41 @@ public class ProtectionFileProviderImpl implements FileProtector.FileProvider {
|
||||
|
||||
@Override
|
||||
public @Nullable File getSignature() {
|
||||
log.info(" !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! GET SIGNATURE FILE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
||||
File signatureFile = SIGNATURE_FILE_PATH.toFile();
|
||||
|
||||
if (signatureFile.exists() && signatureFile.isFile() && signatureFile.length() > 0) {
|
||||
return signatureFile;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull OperationResult writeSignature(@NotNull byte[] bytes) {
|
||||
log.info(" !!!!!!!!!!!!!!!!!!!!!!!!!!! WRITING SIGNATURE FILE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
||||
log.info(" !!!!!!!!!!!!!!!!!!!!!!!!!!! WRITING SIGNATURE FILE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
||||
log.info(" !!!!!!!!!!!!!!!!!!!!!!!!!!! WRITING SIGNATURE FILE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
||||
log.info(" !!!!!!!!!!!!!!!!!!!!!!!!!!! WRITING SIGNATURE FILE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
||||
log.info(" !!!!!!!!!!!!!!!!!!!!!!!!!!! WRITING SIGNATURE FILE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
||||
log.info(" !!!!!!!!!!!!!!!!!!!!!!!!!!! WRITING SIGNATURE FILE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
||||
log.info(" !!!!!!!!!!!!!!!!!!!!!!!!!!! WRITING SIGNATURE FILE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
||||
log.info(" !!!!!!!!!!!!!!!!!!!!!!!!!!! WRITING SIGNATURE FILE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
||||
try {
|
||||
Path directory = SIGNATURE_FILE_PATH.getParent();
|
||||
|
||||
if (directory != null && !Files.exists(directory)) {
|
||||
Files.createDirectories(directory);
|
||||
}
|
||||
|
||||
Files.write(SIGNATURE_FILE_PATH, bytes);
|
||||
|
||||
return OperationResult.Companion.success();
|
||||
} catch (IOException e) {
|
||||
return OperationResult.Companion.failure("Not saved signature file");
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public com.vrt.fileprotection.OperationResult writeAudioFile(@NotNull String id, @NotNull byte[] data, @Nullable String fileExt) {
|
||||
@@ -76,9 +114,4 @@ public class ProtectionFileProviderImpl implements FileProtector.FileProvider {
|
||||
return OperationResult.Companion.failure("Failed to create protected file");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull OperationResult writeSignature(@NotNull byte[] bytes) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,9 @@ public class FileUtil {
|
||||
return new FileProtector.FileInfo(type, fileEntity.getId(), String.valueOf(fileEntity.getUserId()));
|
||||
}
|
||||
|
||||
public int hamming64(int aHi, int aLo, int bHi, int bLo) {
|
||||
return Integer.bitCount(aHi ^ bHi)
|
||||
+ Integer.bitCount(aLo ^ bLo);
|
||||
public int hamming64(long aHi, long aLo, long bHi, long bLo) {
|
||||
return Long.bitCount(aHi ^ bHi)
|
||||
+ Long.bitCount(aLo ^ bLo);
|
||||
}
|
||||
|
||||
private FileProtector.Type determineFileType(String mimeType) {
|
||||
|
||||
Reference in New Issue
Block a user