Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab3eef1668 | ||
|
|
63c3794aa4 | ||
|
|
d638819ee9 | ||
|
|
51654fd060 | ||
|
|
e37cc06cd0 | ||
|
|
5a3e26b6a3 |
@@ -13,13 +13,6 @@ java {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//dependencyManagement {
|
|
||||||
// imports {
|
|
||||||
// mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4"
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
compileOnly {
|
compileOnly {
|
||||||
extendsFrom annotationProcessor
|
extendsFrom annotationProcessor
|
||||||
@@ -62,9 +55,6 @@ dependencies {
|
|||||||
testImplementation 'org.mockito:mockito-core:5.3.1'
|
testImplementation 'org.mockito:mockito-core:5.3.1'
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||||
|
|
||||||
// implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.8.21'
|
|
||||||
// implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
|
|
||||||
// implementation 'io.insert-koin:koin-core:3.5.0'
|
|
||||||
implementation name: 'testlib-fat-0.2.1-all'
|
implementation name: 'testlib-fat-0.2.1-all'
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package ru.soune.nocopy.controller;
|
|||||||
|
|
||||||
import com.vrt.NoCopyFileService;
|
import com.vrt.NoCopyFileService;
|
||||||
import com.vrt.fileprotection.FileProtector;
|
import com.vrt.fileprotection.FileProtector;
|
||||||
|
import com.vrt.fileprotection.NoCopyCheckResult;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.core.io.Resource;
|
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.service.file.FileUploadService;
|
||||||
import ru.soune.nocopy.util.FileUtil;
|
import ru.soune.nocopy.util.FileUtil;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@@ -380,6 +382,25 @@ public class ApiController {
|
|||||||
return ResponseEntity.ok().build();
|
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.getProtectedFilePath());
|
||||||
|
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 {
|
private ResponseEntity<BaseResponse> checkForDuplicates(String uploadId) throws IOException {
|
||||||
Optional<FileEntity> uploadedFile = fileEntityRepository.findByUploadSessionId(uploadId);
|
Optional<FileEntity> uploadedFile = fileEntityRepository.findByUploadSessionId(uploadId);
|
||||||
|
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
package ru.soune.nocopy.entity;
|
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
|
||||||
import lombok.*;
|
|
||||||
import org.springframework.data.annotation.CreatedDate;
|
|
||||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
@Table(name = "image_protection")
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
@ToString
|
|
||||||
@EntityListeners(AuditingEntityListener.class)
|
|
||||||
public class ImageProtection {
|
|
||||||
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
private Long protectionId;
|
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY)
|
|
||||||
@JoinColumn(name = "user_id", nullable = false)
|
|
||||||
@ToString.Exclude
|
|
||||||
private User user;
|
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY)
|
|
||||||
@JoinColumn(name = "content_id", nullable = false)
|
|
||||||
@ToString.Exclude
|
|
||||||
private UserContent content;
|
|
||||||
|
|
||||||
@Column(name = "protection_method", nullable = false, length = 50)
|
|
||||||
private String protectionMethod;
|
|
||||||
|
|
||||||
@Column(name = "protection_level", nullable = false)
|
|
||||||
private Integer protectionLevel;
|
|
||||||
|
|
||||||
@Column(name = "is_active", nullable = false)
|
|
||||||
private Boolean isActive = true;
|
|
||||||
|
|
||||||
@CreatedDate
|
|
||||||
@Column(name = "applied_at", nullable = false, updatable = false)
|
|
||||||
private LocalDateTime appliedAt;
|
|
||||||
|
|
||||||
@Column(name = "metadata", columnDefinition = "JSON")
|
|
||||||
private String metadata;
|
|
||||||
}
|
|
||||||
@@ -84,10 +84,4 @@ public class User {
|
|||||||
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
|
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
@ToString.Exclude
|
@ToString.Exclude
|
||||||
private List<Violation> violations = new ArrayList<>();
|
private List<Violation> violations = new ArrayList<>();}
|
||||||
|
|
||||||
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
|
|
||||||
@JsonIgnore
|
|
||||||
@ToString.Exclude
|
|
||||||
private List<ImageProtection> imageProtections = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public enum FileType {
|
|||||||
IMAGE("image", Arrays.asList("jpg", "jpeg", "png", "gif", "bmp", "webp", "jfif")),
|
IMAGE("image", Arrays.asList("jpg", "jpeg", "png", "gif", "bmp", "webp", "jfif")),
|
||||||
VIDEO("video", Arrays.asList("mp4", "avi", "mov", "wmv", "flv", "mkv", "webm", "m4v", "mpg", "mpeg",
|
VIDEO("video", Arrays.asList("mp4", "avi", "mov", "wmv", "flv", "mkv", "webm", "m4v", "mpg", "mpeg",
|
||||||
"3gp", "3g2", "f4v", "m2ts", "mts", "vob", "ogv", "divx")),
|
"3gp", "3g2", "f4v", "m2ts", "mts", "vob", "ogv", "divx")),
|
||||||
AUDIO("audio", Arrays.asList("mp3", "wav", "flac"));
|
AUDIO("audio", List.of("wav"));
|
||||||
|
|
||||||
private final String displayName;
|
private final String displayName;
|
||||||
private final List<String> allowedExtensions;
|
private final List<String> allowedExtensions;
|
||||||
|
|||||||
@@ -26,10 +26,10 @@ public class ImageHashEntity {
|
|||||||
private FileEntity file;
|
private FileEntity file;
|
||||||
|
|
||||||
@Column(name = "hash64_hi")
|
@Column(name = "hash64_hi")
|
||||||
private Integer hash64Hi;
|
private Long hash64Hi;
|
||||||
|
|
||||||
@Column(name = "hash64_lo")
|
@Column(name = "hash64_lo")
|
||||||
private Integer hash64Lo;
|
private Long hash64Lo;
|
||||||
|
|
||||||
@Column(name = "hash_algorithm", nullable = false)
|
@Column(name = "hash_algorithm", nullable = false)
|
||||||
private String hashAlgorithm;
|
private String hashAlgorithm;
|
||||||
|
|||||||
@@ -10,5 +10,5 @@ import java.util.List;
|
|||||||
public interface ImageHashRepository extends JpaRepository<ImageHashEntity, String> {
|
public interface ImageHashRepository extends JpaRepository<ImageHashEntity, String> {
|
||||||
void deleteByFileId(String fileId);
|
void deleteByFileId(String fileId);
|
||||||
List<ImageHashEntity> findByFileId(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)
|
nativeQuery = true)
|
||||||
List<SimilarImageProjection> findExactDuplicates(
|
List<SimilarImageProjection> findExactDuplicates(
|
||||||
@Param("hash64Hi") Integer hash64_hi,
|
@Param("hash64Hi") Long hash64_hi,
|
||||||
@Param("hash64Lo") Integer hash64_lo);
|
@Param("hash64Lo") Long hash64_lo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package ru.soune.nocopy.repository;
|
package ru.soune.nocopy.repository;
|
||||||
|
|
||||||
public interface SimilarImageProjection {
|
public interface SimilarImageProjection {
|
||||||
Integer getHash64Hi();
|
Long getHash64Hi();
|
||||||
Integer getHash64Lo();
|
Long getHash64Lo();
|
||||||
String getFileId();
|
String getFileId();
|
||||||
Long getUserId();
|
Long getUserId();
|
||||||
String getOriginalFileName();
|
String getOriginalFileName();
|
||||||
|
|||||||
@@ -32,15 +32,15 @@ public class FileSimilarityService {
|
|||||||
var imageHashEntity = hashRepository.findById(fileId)
|
var imageHashEntity = hashRepository.findById(fileId)
|
||||||
.orElseThrow(() -> new RuntimeException("Hash not found"));
|
.orElseThrow(() -> new RuntimeException("Hash not found"));
|
||||||
|
|
||||||
Integer hash64Hi = imageHashEntity.getHash64Hi();
|
Long hash64Hi = imageHashEntity.getHash64Hi();
|
||||||
Integer hash64Lo = imageHashEntity.getHash64Lo();
|
Long hash64Lo = imageHashEntity.getHash64Lo();
|
||||||
|
|
||||||
List<SimilarImageProjection> candidates = repository.findCandidates(fileId);
|
List<SimilarImageProjection> candidates = repository.findCandidates(fileId);
|
||||||
|
|
||||||
return candidates.stream()
|
return candidates.stream()
|
||||||
.map(c -> {
|
.map(c -> {
|
||||||
Integer cHi = c.getHash64Hi();
|
Long cHi = c.getHash64Hi();
|
||||||
Integer cLo = c.getHash64Lo();
|
Long cLo = c.getHash64Lo();
|
||||||
|
|
||||||
int hamming = fileUtil.hamming64(hash64Hi, hash64Lo, cHi, cLo);
|
int hamming = fileUtil.hamming64(hash64Hi, hash64Lo, cHi, cLo);
|
||||||
|
|
||||||
@@ -73,13 +73,13 @@ public class FileSimilarityService {
|
|||||||
var imageHashEntity = hashRepository.findById(fileId)
|
var imageHashEntity = hashRepository.findById(fileId)
|
||||||
.orElseThrow(() -> new RuntimeException("Hash not found"));
|
.orElseThrow(() -> new RuntimeException("Hash not found"));
|
||||||
|
|
||||||
Integer hash64Hi = imageHashEntity.getHash64Hi();
|
Long hash64Hi = imageHashEntity.getHash64Hi();
|
||||||
Integer hash64Lo = imageHashEntity.getHash64Lo();
|
Long hash64Lo = imageHashEntity.getHash64Lo();
|
||||||
|
|
||||||
return candidates.stream()
|
return candidates.stream()
|
||||||
.map(c -> {
|
.map(c -> {
|
||||||
Integer cHi = c.getHash64Hi();
|
Long cHi = c.getHash64Hi();
|
||||||
Integer cLo = c.getHash64Lo();
|
Long cLo = c.getHash64Lo();
|
||||||
|
|
||||||
int hamming = fileUtil.hamming64(hash64Hi, hash64Lo, cHi, cLo);
|
int hamming = fileUtil.hamming64(hash64Hi, hash64Lo, cHi, cLo);
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ public class FileSimilarityService {
|
|||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<SimilarImageProjection> findDuplicatedByHash(Integer hash64Hi, Integer hash64Lo) {
|
public List<SimilarImageProjection> findDuplicatedByHash(Long hash64Hi, Long hash64Lo) {
|
||||||
List<SimilarImageProjection> duplicates = repository.findExactDuplicates(hash64Hi,hash64Lo);
|
List<SimilarImageProjection> duplicates = repository.findExactDuplicates(hash64Hi,hash64Lo);
|
||||||
|
|
||||||
if (duplicates.isEmpty()) {
|
if (duplicates.isEmpty()) {
|
||||||
@@ -126,8 +126,8 @@ public class FileSimilarityService {
|
|||||||
var imageHashEntity = hashRepository.findById(fileId)
|
var imageHashEntity = hashRepository.findById(fileId)
|
||||||
.orElseThrow(() -> new RuntimeException("Hash not found"));
|
.orElseThrow(() -> new RuntimeException("Hash not found"));
|
||||||
|
|
||||||
Integer hash64Hi = imageHashEntity.getHash64Hi();
|
Long hash64Hi = imageHashEntity.getHash64Hi();
|
||||||
Integer hash64Lo = imageHashEntity.getHash64Lo();
|
Long hash64Lo = imageHashEntity.getHash64Lo();
|
||||||
|
|
||||||
List<SimilarImageProjection> candidates = repository.findCandidates(fileId);
|
List<SimilarImageProjection> candidates = repository.findCandidates(fileId);
|
||||||
|
|
||||||
@@ -154,9 +154,9 @@ public class FileSimilarityService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private SimilarFileDTO createSimilarFileResponse(SimilarImageProjection similarImageProjection,
|
private SimilarFileDTO createSimilarFileResponse(SimilarImageProjection similarImageProjection,
|
||||||
Integer hash64Hi, Integer hash64Lo) {
|
Long hash64Hi, Long hash64Lo) {
|
||||||
Integer imageProjectionHash64Hi = similarImageProjection.getHash64Hi();
|
Long imageProjectionHash64Hi = similarImageProjection.getHash64Hi();
|
||||||
Integer similarImageProjectionHash64Lo = similarImageProjection.getHash64Lo();
|
Long similarImageProjectionHash64Lo = similarImageProjection.getHash64Lo();
|
||||||
|
|
||||||
int hamming = fileUtil.hamming64(hash64Hi, hash64Lo, imageProjectionHash64Hi, similarImageProjectionHash64Lo);
|
int hamming = fileUtil.hamming64(hash64Hi, hash64Lo, imageProjectionHash64Hi, similarImageProjectionHash64Lo);
|
||||||
|
|
||||||
|
|||||||
@@ -22,19 +22,19 @@ public class ImageHashService {
|
|||||||
|
|
||||||
private final ImageHashRepository repository;
|
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();
|
File file = imagePath.toFile();
|
||||||
PHash pHash = PerceptualHashHelper.INSTANCE.generateDCTPerceptualHash(file);
|
PHash pHash = PerceptualHashHelper.INSTANCE.generateDCTPerceptualHash(file);
|
||||||
Long firstPart = pHash.getFirstPart();
|
Long firstPart = pHash.getFirstPart();
|
||||||
Long secondPart = pHash.getSecondPart();
|
Long secondPart = pHash.getSecondPart();
|
||||||
Map<String, Integer> hash = Map.of(
|
Map<String, Long> hash = Map.of(
|
||||||
"hi", Math.toIntExact(firstPart),
|
"hi", firstPart,
|
||||||
"low", Math.toIntExact(secondPart));
|
"low", secondPart);
|
||||||
|
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void create(FileEntity file, Map<String, Integer> stringIntegerMap) {
|
public void create(FileEntity file, Map<String, Long> stringIntegerMap) {
|
||||||
ImageHashEntity entity = ImageHashEntity.builder()
|
ImageHashEntity entity = ImageHashEntity.builder()
|
||||||
.file(file)
|
.file(file)
|
||||||
.hash64Hi(stringIntegerMap.get("hi"))
|
.hash64Hi(stringIntegerMap.get("hi"))
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import ru.soune.nocopy.dto.BaseResponse;
|
|||||||
import ru.soune.nocopy.dto.MessageCode;
|
import ru.soune.nocopy.dto.MessageCode;
|
||||||
import ru.soune.nocopy.dto.file.YandexSearchResponse;
|
import ru.soune.nocopy.dto.file.YandexSearchResponse;
|
||||||
import ru.soune.nocopy.entity.file.FileEntity;
|
import ru.soune.nocopy.entity.file.FileEntity;
|
||||||
import ru.soune.nocopy.exception.FileEntityNotFoundException;
|
|
||||||
import ru.soune.nocopy.exception.NotValidFieldException;
|
import ru.soune.nocopy.exception.NotValidFieldException;
|
||||||
import ru.soune.nocopy.repository.FileEntityRepository;
|
import ru.soune.nocopy.repository.FileEntityRepository;
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public class FileEntityService {
|
|||||||
throw new IOException("File not found on disk: " + filePath);
|
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")) {
|
if (session.getFileType().startsWith("image")) {
|
||||||
imageHash = imageHashService.calculateHash(filePath);
|
imageHash = imageHashService.calculateHash(filePath);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import ru.soune.nocopy.entity.file.FileEntity;
|
import ru.soune.nocopy.entity.file.FileEntity;
|
||||||
import ru.soune.nocopy.entity.file.ProtectionStatus;
|
import ru.soune.nocopy.entity.file.ProtectionStatus;
|
||||||
import ru.soune.nocopy.repository.FileEntityRepository;
|
import ru.soune.nocopy.repository.FileEntityRepository;
|
||||||
|
import ru.soune.nocopy.util.FileUtil;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -21,12 +22,14 @@ public class FileProcessingOrchestrator {
|
|||||||
|
|
||||||
private final FileEntityRepository fileRepository;
|
private final FileEntityRepository fileRepository;
|
||||||
|
|
||||||
|
private final FileUtil fileUtil;
|
||||||
|
|
||||||
public void initializeProcessingQueue() {
|
public void initializeProcessingQueue() {
|
||||||
List<FileEntity> filesToProtect = fileRepository.findByProtectionStatus(ProtectionStatus.NOT_PROTECTED);
|
List<FileEntity> filesToProtect = fileRepository.findByProtectionStatus(ProtectionStatus.NOT_PROTECTED);
|
||||||
|
|
||||||
for (FileEntity fileEntity : filesToProtect) {
|
for (FileEntity fileEntity : filesToProtect) {
|
||||||
try {
|
try {
|
||||||
FileProtector.FileInfo fileInfo = createFileInfo(fileEntity);
|
FileProtector.FileInfo fileInfo = fileUtil.createFileInfo(fileEntity);
|
||||||
|
|
||||||
noCopyFileService.addFile(fileInfo);
|
noCopyFileService.addFile(fileInfo);
|
||||||
|
|
||||||
@@ -42,31 +45,9 @@ public class FileProcessingOrchestrator {
|
|||||||
List<FileEntity> newFiles = fileRepository.findAllActiveFilesAndNotProtected();
|
List<FileEntity> newFiles = fileRepository.findAllActiveFilesAndNotProtected();
|
||||||
|
|
||||||
for (FileEntity fileEntity : newFiles) {
|
for (FileEntity fileEntity : newFiles) {
|
||||||
FileProtector.FileInfo fileInfo = createFileInfo(fileEntity);
|
FileProtector.FileInfo fileInfo = fileUtil.createFileInfo(fileEntity);
|
||||||
|
|
||||||
noCopyFileService.addFile(fileInfo);
|
noCopyFileService.addFile(fileInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private FileProtector.FileInfo createFileInfo(FileEntity fileEntity) {
|
|
||||||
FileProtector.Type type = determineFileType(fileEntity.getMimeType());
|
|
||||||
|
|
||||||
return new FileProtector.FileInfo(type, fileEntity.getId(), String.valueOf(fileEntity.getUserId()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private FileProtector.Type determineFileType(String mimeType) {
|
|
||||||
if (mimeType == null) {
|
|
||||||
return FileProtector.Type.IMAGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mimeType.startsWith("image")) {
|
|
||||||
return FileProtector.Type.IMAGE;
|
|
||||||
} else if (mimeType.startsWith("video")) {
|
|
||||||
return FileProtector.Type.VIDEO;
|
|
||||||
} else if (mimeType.startsWith("audio")) {
|
|
||||||
return FileProtector.Type.AUDIO;
|
|
||||||
} else {
|
|
||||||
return FileProtector.Type.IMAGE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import ru.soune.nocopy.entity.file.FileEntity;
|
||||||
import ru.soune.nocopy.service.file.FileEntityService;
|
import ru.soune.nocopy.service.file.FileEntityService;
|
||||||
import ru.soune.nocopy.util.FileUtil;
|
import ru.soune.nocopy.util.FileUtil;
|
||||||
|
|
||||||
@@ -23,7 +24,9 @@ public class AudioLocalSearchImpl implements AudioLocalSearch {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable FileProtector.FileInfo findBySignature(@NotNull String signature) {
|
public @Nullable FileProtector.FileInfo findBySignature(@NotNull String signature) {
|
||||||
return fileUtil.createFileInfo(fileEntityService.findBySignature(signature));
|
FileEntity fileEntity = fileEntityService.findBySignature(signature);
|
||||||
|
|
||||||
|
return fileUtil.createFileInfo(fileEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package ru.soune.nocopy.service.file.impl;
|
package ru.soune.nocopy.service.file.impl;
|
||||||
|
|
||||||
|
import com.vrt.NoCopyFileService;
|
||||||
|
import com.vrt.fileprotection.FileProtector;
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -25,6 +27,7 @@ import ru.soune.nocopy.service.FileSimilarityService;
|
|||||||
import ru.soune.nocopy.service.ImageHashService;
|
import ru.soune.nocopy.service.ImageHashService;
|
||||||
import ru.soune.nocopy.service.file.FileEntityService;
|
import ru.soune.nocopy.service.file.FileEntityService;
|
||||||
import ru.soune.nocopy.service.file.FileUploadService;
|
import ru.soune.nocopy.service.file.FileUploadService;
|
||||||
|
import ru.soune.nocopy.util.FileUtil;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.file.*;
|
import java.nio.file.*;
|
||||||
@@ -75,6 +78,10 @@ public class FileUploadServiceImpl implements FileUploadService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private FileEntityService fileEntityService;
|
private FileEntityService fileEntityService;
|
||||||
|
|
||||||
|
private final NoCopyFileService noCopyFileService;
|
||||||
|
|
||||||
|
private final FileUtil fileUtil;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
try {
|
try {
|
||||||
@@ -276,11 +283,16 @@ public class FileUploadServiceImpl implements FileUploadService {
|
|||||||
|
|
||||||
FileEntity saved = fileEntityRepository.save(fileEntity);
|
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);
|
cleanupSessionFiles(session);
|
||||||
|
|
||||||
|
noCopyFileService.addFile(fileUtil.createFileInfo(fileEntity));
|
||||||
|
|
||||||
log.info("File processing completed for session: {}", session.getUploadId());
|
log.info("File processing completed for session: {}", session.getUploadId());
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -372,7 +384,8 @@ public class FileUploadServiceImpl implements FileUploadService {
|
|||||||
private void checkForDuplicatesSynchronously(String filePath)
|
private void checkForDuplicatesSynchronously(String filePath)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
Path path = Paths.get(filePath);
|
Path path = Paths.get(filePath);
|
||||||
Map<String, Integer> hash = imageHashService.calculateHash(path);
|
Map<String, Long> hash = imageHashService.calculateHash(path);
|
||||||
|
|
||||||
List<SimilarImageProjection> duplicates = fileSimilarityService.findDuplicatedByHash(
|
List<SimilarImageProjection> duplicates = fileSimilarityService.findDuplicatedByHash(
|
||||||
hash.get("hi"), hash.get("low"));
|
hash.get("hi"), hash.get("low"));
|
||||||
|
|
||||||
@@ -484,11 +497,17 @@ public class FileUploadServiceImpl implements FileUploadService {
|
|||||||
session.setChecksum(checksum);
|
session.setChecksum(checksum);
|
||||||
session.setStatus(UploadStatus.COMPLETED);
|
session.setStatus(UploadStatus.COMPLETED);
|
||||||
session.setCompletedAt(LocalDateTime.now());
|
session.setCompletedAt(LocalDateTime.now());
|
||||||
|
|
||||||
sessionRepository.save(session);
|
sessionRepository.save(session);
|
||||||
log.info("Upload session updated to COMPLETED: {}", session.getUploadId());
|
log.info("Upload session updated to COMPLETED: {}", session.getUploadId());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fileEntityService.createFromUploadSession(session, checksum);
|
FileEntity fileEntity = fileEntityService.createFromUploadSession(session, checksum);
|
||||||
|
|
||||||
|
FileProtector.FileInfo fileInfo = fileUtil.createFileInfo(fileEntity);
|
||||||
|
|
||||||
|
noCopyFileService.addFile(fileInfo);
|
||||||
|
|
||||||
log.info("FileEntity successfully created for session: {}",
|
log.info("FileEntity successfully created for session: {}",
|
||||||
session.getUploadId());
|
session.getUploadId());
|
||||||
} catch (DuplicateImageException e) {
|
} catch (DuplicateImageException e) {
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ public class ImageLocalSearchImpl implements ImageLocalSearch {
|
|||||||
@Override
|
@Override
|
||||||
public @Nullable FileProtector.FileInfo findByPHash(@NotNull PHash pHash) {
|
public @Nullable FileProtector.FileInfo findByPHash(@NotNull PHash pHash) {
|
||||||
ImageHashEntity imageHashEntity =
|
ImageHashEntity imageHashEntity =
|
||||||
imageHashRepository.findByHash64HiAndHash64Lo(Math.toIntExact(pHash.getFirstPart()),
|
imageHashRepository.findByHash64HiAndHash64Lo(pHash.getFirstPart(),
|
||||||
Math.toIntExact(pHash.getSecondPart()));
|
pHash.getSecondPart());
|
||||||
FileEntity file = fileEntityRepository.findByFileId(imageHashEntity.getFileId());
|
FileEntity file = fileEntityRepository.findByFileId(imageHashEntity.getFileId());
|
||||||
|
|
||||||
return new FileProtector.FileInfo(FileProtector.Type.IMAGE, 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 ru.soune.nocopy.service.file.FileEntityService;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class ProtectionFileProviderImpl implements FileProtector.FileProvider {
|
public class ProtectionFileProviderImpl implements FileProtector.FileProvider {
|
||||||
|
|
||||||
|
private static final Path SIGNATURE_FILE_PATH = Paths.get("/data/uploads/signature");
|
||||||
|
|
||||||
private final FileEntityService fileEntityService;
|
private final FileEntityService fileEntityService;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -38,9 +44,32 @@ public class ProtectionFileProviderImpl implements FileProtector.FileProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable File getSignature() {
|
public @Nullable File getSignature() {
|
||||||
|
File signatureFile = SIGNATURE_FILE_PATH.toFile();
|
||||||
|
|
||||||
|
if (signatureFile.exists() && signatureFile.isFile() && signatureFile.length() > 0) {
|
||||||
|
return signatureFile;
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull OperationResult writeSignature(@NotNull byte[] bytes) {
|
||||||
|
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
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public com.vrt.fileprotection.OperationResult writeAudioFile(@NotNull String id, @NotNull byte[] data, @Nullable String fileExt) {
|
public com.vrt.fileprotection.OperationResult writeAudioFile(@NotNull String id, @NotNull byte[] data, @Nullable String fileExt) {
|
||||||
@@ -76,9 +105,4 @@ public class ProtectionFileProviderImpl implements FileProtector.FileProvider {
|
|||||||
return OperationResult.Companion.failure("Failed to create protected file");
|
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()));
|
return new FileProtector.FileInfo(type, fileEntity.getId(), String.valueOf(fileEntity.getUserId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public int hamming64(int aHi, int aLo, int bHi, int bLo) {
|
public int hamming64(long aHi, long aLo, long bHi, long bLo) {
|
||||||
return Integer.bitCount(aHi ^ bHi)
|
return Long.bitCount(aHi ^ bHi)
|
||||||
+ Integer.bitCount(aLo ^ bLo);
|
+ Long.bitCount(aLo ^ bLo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private FileProtector.Type determineFileType(String mimeType) {
|
private FileProtector.Type determineFileType(String mimeType) {
|
||||||
|
|||||||
Reference in New Issue
Block a user