Skip to content
This repository was archived by the owner on Apr 20, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: docker-test-perf build up clean compile update test
.PHONY: docker-test-perf build up clean compile update test test-duplicate

build:
docker-compose build
Expand All @@ -22,5 +22,8 @@ update:
docker-test-perf:
docker-compose exec app mvn test -f /source/pom.xml -Dtest=ChunkingPerformanceTest

test-duplicate:
docker-compose exec app mvn test -f /source/pom.xml -Dtest=DuplicationPerformanceTest

test:
docker-compose exec app mvn test -f /source/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.goofy.GoofyFiles.compression.CompressionService;
import com.goofy.GoofyFiles.duplication.DuplicationService;
import com.goofy.GoofyFiles.duplication.HashingAlgorithm;

Expand Down Expand Up @@ -63,4 +64,28 @@ public ResponseEntity<?> processFile(
.body(Map.of("error", "Échec du traitement et de l'enregistrement du fichier: " + e.getMessage()));
}
}
}

@PostMapping("/process-compressed")
public ResponseEntity<?> processFileCompressed(
@RequestParam("file") MultipartFile file,
@RequestParam(value = "algorithm", defaultValue = "SHA256") HashingAlgorithm algorithm,
@RequestParam(value = "compression", defaultValue = "LZ4") CompressionService.CompressionType compression) {
try {
File tempFile = File.createTempFile("upload-", "-" + file.getOriginalFilename());
file.transferTo(tempFile);

Map<String, Object> result = duplicationService.processAndStoreFileCompressed(
tempFile,
file.getOriginalFilename(),
file.getSize(),
algorithm,
compression);

tempFile.delete();
return ResponseEntity.ok(result);
} catch (IOException e) {
return ResponseEntity.internalServerError()
.body(Map.of("error", "Échec du traitement et de l'enregistrement du fichier compressé: " + e.getMessage()));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

import com.goofy.GoofyFiles.chunking.Chunk;
import com.goofy.GoofyFiles.chunking.ChunkingService;
import com.goofy.GoofyFiles.compression.CompressionService;
import com.goofy.GoofyFiles.compression.CompressionService.CompressionType;
import com.goofy.GoofyFiles.model.ChunkEntity;
import com.goofy.GoofyFiles.model.FileChunkEntity;
import com.goofy.GoofyFiles.model.FileEntity;
Expand All @@ -35,6 +37,7 @@ public class DuplicationService {
private final FileRepository fileRepository;
private final ChunkRepository chunkRepository;
private final FileChunkRepository fileChunkRepository;
private final CompressionService compressionService;

/**
* Constructeur principal pour l'utilisation en production
Expand All @@ -44,11 +47,13 @@ public DuplicationService(
ChunkingService chunkingService,
FileRepository fileRepository,
ChunkRepository chunkRepository,
FileChunkRepository fileChunkRepository) {
FileChunkRepository fileChunkRepository,
CompressionService compressionService) {
this.chunkingService = chunkingService;
this.fileRepository = fileRepository;
this.chunkRepository = chunkRepository;
this.fileChunkRepository = fileChunkRepository;
this.compressionService = compressionService;
}

/**
Expand All @@ -57,10 +62,7 @@ public DuplicationService(
* pas disponibles
*/
public DuplicationService(ChunkingService chunkingService) {
this.chunkingService = chunkingService;
this.fileRepository = null;
this.chunkRepository = null;
this.fileChunkRepository = null;
this(chunkingService, null, null, null, null);
}

public Map<String, Object> analyzeFile(File file, HashingAlgorithm algorithm) throws IOException {
Expand All @@ -74,8 +76,6 @@ public Map<String, Object> analyzeFile(File file, HashingAlgorithm algorithm) th
chunk.getPosition(), chunk.getData().length, hash);
}

// Filtrer les chunks qui apparaissent plus d'une fois (vous pouvez logguer ou
// utiliser ce résultat)
duplicates.entrySet().stream()
.filter(e -> e.getValue() > 1);

Expand Down Expand Up @@ -106,7 +106,6 @@ private String calculateHash(byte[] data, HashingAlgorithm algorithm) {
case SHA256:
return Hashing.sha256().hashBytes(data).toString();
case BLAKE3:
// Utilisation de Apache Commons Codec pour BLAKE3
byte[] hashBytes = Blake3.hash(data);
return Hex.encodeHexString(hashBytes);
default:
Expand Down Expand Up @@ -174,7 +173,6 @@ public Map<String, Object> processAndStoreFile(
existingChunk = Optional.empty();
}

// Traiter le chunk (nouveau ou existant)
ChunkEntity chunkEntity;
if (existingChunk.isPresent()) {
chunkEntity = existingChunk.get();
Expand Down Expand Up @@ -228,4 +226,128 @@ public Map<String, Object> processAndStoreFile(

return result;
}
}

@Transactional
public Map<String, Object> processAndStoreFileCompressed(
File file,
String fileName,
long fileSize,
HashingAlgorithm algorithm,
CompressionType compressionType) throws IOException {
if (fileRepository == null || chunkRepository == null || fileChunkRepository == null
|| compressionService == null) {
throw new UnsupportedOperationException(
"Cette méthode nécessite les repositories et le service de compression qui n'ont pas été injectés. " +
"Utilisez le constructeur avec tous les paramètres pour cette fonctionnalité.");
}

// 1. Extraire le nom et l'extension
String name = fileName;
String extension = "";
int lastDotIndex = fileName.lastIndexOf('.');
if (lastDotIndex > 0) {
name = fileName.substring(0, lastDotIndex);
extension = fileName.substring(lastDotIndex + 1);
}

// 2. Créer et sauvegarder l'entité de fichier
FileEntity fileEntity = new FileEntity();
fileEntity.setName(name);
fileEntity.setExtension(extension);
fileEntity.setSize(fileSize);
fileEntity = fileRepository.save(fileEntity);

// 3. Découper le fichier
List<Chunk> chunks = chunkingService.chunkFile(file);

// Statistiques pour le résultat
int totalChunks = chunks.size();
int duplicateChunks = 0;
int uniqueChunks = 0;
long savedStorage = 0;
long totalCompressedSize = 0;

// 4. Traiter chaque chunk
for (Chunk chunk : chunks) {
String hash = calculateHash(chunk.getData(), algorithm);

// Chercher si ce chunk existe déjà en base
Optional<ChunkEntity> existingChunk;
switch (algorithm) {
case SHA1:
existingChunk = chunkRepository.findByHashSha1(hash);
break;
case SHA256:
existingChunk = chunkRepository.findByHashSha256(hash);
break;
case BLAKE3:
existingChunk = chunkRepository.findByHashBlake3(hash);
break;
default:
existingChunk = Optional.empty();
}

ChunkEntity chunkEntity;
if (existingChunk.isPresent()) {
chunkEntity = existingChunk.get();
duplicateChunks++;
savedStorage += chunk.getOriginalSize();
logger.info("Chunk dupliqué trouvé: {}", hash);
} else {
// Compression du chunk
byte[] compressedData = compressionService.compress(chunk.getData(), compressionType);
totalCompressedSize += compressedData.length;

chunkEntity = new ChunkEntity();
// Stocker les données compressées
chunkEntity.setData(compressedData);
// Vous pouvez ajouter une propriété pour stocker la taille originale si besoin,
// ex :
// chunkEntity.setOriginalSize(chunk.getData().length);

// Stocker le hash selon l'algorithme
switch (algorithm) {
case SHA1:
chunkEntity.setHashSha1(hash);
break;
case SHA256:
chunkEntity.setHashSha256(hash);
break;
case BLAKE3:
chunkEntity.setHashBlake3(hash);
break;
}

chunkEntity = chunkRepository.save(chunkEntity);
uniqueChunks++;
}

// Créer la relation entre le fichier et le chunk
FileChunkEntity fileChunk = new FileChunkEntity();
fileChunk.setFile(fileEntity);
fileChunk.setChunk(chunkEntity);
fileChunk.setPosition(chunk.getPosition());
fileChunkRepository.save(fileChunk);
}

// 5. Préparer le résultat
Map<String, Object> result = new HashMap<>();
result.put("fileId", fileEntity.getId());
result.put("fileName", fileEntity.getName());
result.put("extension", fileEntity.getExtension());
result.put("fileSize", fileEntity.getSize());
result.put("algorithm", algorithm.name());
result.put("compressionType", compressionType.name());
result.put("totalChunks", totalChunks);
result.put("uniqueChunks", uniqueChunks);
result.put("duplicateChunks", duplicateChunks);
result.put("savedStorage", savedStorage);
result.put("deduplicationRatio", totalChunks > 0 ? (double) duplicateChunks / totalChunks : 0);
result.put("totalCompressedSize", totalCompressedSize);

logger.info("Fichier compressé traité: id={}, nom={}, chunks={}, uniques={}, doublons={}, taille compressée={}",
fileEntity.getId(), fileName, totalChunks, uniqueChunks, duplicateChunks, totalCompressedSize);

return result;
}
}
Loading