1818
1919import com .goofy .GoofyFiles .chunking .Chunk ;
2020import com .goofy .GoofyFiles .chunking .ChunkingService ;
21+ import com .goofy .GoofyFiles .compression .CompressionService ;
22+ import com .goofy .GoofyFiles .compression .CompressionService .CompressionType ;
2123import com .goofy .GoofyFiles .model .ChunkEntity ;
2224import com .goofy .GoofyFiles .model .FileChunkEntity ;
2325import com .goofy .GoofyFiles .model .FileEntity ;
@@ -35,6 +37,7 @@ public class DuplicationService {
3537 private final FileRepository fileRepository ;
3638 private final ChunkRepository chunkRepository ;
3739 private final FileChunkRepository fileChunkRepository ;
40+ private final CompressionService compressionService ;
3841
3942 /**
4043 * Constructeur principal pour l'utilisation en production
@@ -44,11 +47,13 @@ public DuplicationService(
4447 ChunkingService chunkingService ,
4548 FileRepository fileRepository ,
4649 ChunkRepository chunkRepository ,
47- FileChunkRepository fileChunkRepository ) {
50+ FileChunkRepository fileChunkRepository ,
51+ CompressionService compressionService ) {
4852 this .chunkingService = chunkingService ;
4953 this .fileRepository = fileRepository ;
5054 this .chunkRepository = chunkRepository ;
5155 this .fileChunkRepository = fileChunkRepository ;
56+ this .compressionService = compressionService ;
5257 }
5358
5459 /**
@@ -57,10 +62,7 @@ public DuplicationService(
5762 * pas disponibles
5863 */
5964 public DuplicationService (ChunkingService chunkingService ) {
60- this .chunkingService = chunkingService ;
61- this .fileRepository = null ;
62- this .chunkRepository = null ;
63- this .fileChunkRepository = null ;
65+ this (chunkingService , null , null , null , null );
6466 }
6567
6668 public Map <String , Object > analyzeFile (File file , HashingAlgorithm algorithm ) throws IOException {
@@ -74,8 +76,6 @@ public Map<String, Object> analyzeFile(File file, HashingAlgorithm algorithm) th
7476 chunk .getPosition (), chunk .getData ().length , hash );
7577 }
7678
77- // Filtrer les chunks qui apparaissent plus d'une fois (vous pouvez logguer ou
78- // utiliser ce résultat)
7979 duplicates .entrySet ().stream ()
8080 .filter (e -> e .getValue () > 1 );
8181
@@ -106,7 +106,6 @@ private String calculateHash(byte[] data, HashingAlgorithm algorithm) {
106106 case SHA256 :
107107 return Hashing .sha256 ().hashBytes (data ).toString ();
108108 case BLAKE3 :
109- // Utilisation de Apache Commons Codec pour BLAKE3
110109 byte [] hashBytes = Blake3 .hash (data );
111110 return Hex .encodeHexString (hashBytes );
112111 default :
@@ -174,7 +173,6 @@ public Map<String, Object> processAndStoreFile(
174173 existingChunk = Optional .empty ();
175174 }
176175
177- // Traiter le chunk (nouveau ou existant)
178176 ChunkEntity chunkEntity ;
179177 if (existingChunk .isPresent ()) {
180178 chunkEntity = existingChunk .get ();
@@ -228,4 +226,128 @@ public Map<String, Object> processAndStoreFile(
228226
229227 return result ;
230228 }
231- }
229+
230+ @ Transactional
231+ public Map <String , Object > processAndStoreFileCompressed (
232+ File file ,
233+ String fileName ,
234+ long fileSize ,
235+ HashingAlgorithm algorithm ,
236+ CompressionType compressionType ) throws IOException {
237+ if (fileRepository == null || chunkRepository == null || fileChunkRepository == null
238+ || compressionService == null ) {
239+ throw new UnsupportedOperationException (
240+ "Cette méthode nécessite les repositories et le service de compression qui n'ont pas été injectés. " +
241+ "Utilisez le constructeur avec tous les paramètres pour cette fonctionnalité." );
242+ }
243+
244+ // 1. Extraire le nom et l'extension
245+ String name = fileName ;
246+ String extension = "" ;
247+ int lastDotIndex = fileName .lastIndexOf ('.' );
248+ if (lastDotIndex > 0 ) {
249+ name = fileName .substring (0 , lastDotIndex );
250+ extension = fileName .substring (lastDotIndex + 1 );
251+ }
252+
253+ // 2. Créer et sauvegarder l'entité de fichier
254+ FileEntity fileEntity = new FileEntity ();
255+ fileEntity .setName (name );
256+ fileEntity .setExtension (extension );
257+ fileEntity .setSize (fileSize );
258+ fileEntity = fileRepository .save (fileEntity );
259+
260+ // 3. Découper le fichier
261+ List <Chunk > chunks = chunkingService .chunkFile (file );
262+
263+ // Statistiques pour le résultat
264+ int totalChunks = chunks .size ();
265+ int duplicateChunks = 0 ;
266+ int uniqueChunks = 0 ;
267+ long savedStorage = 0 ;
268+ long totalCompressedSize = 0 ;
269+
270+ // 4. Traiter chaque chunk
271+ for (Chunk chunk : chunks ) {
272+ String hash = calculateHash (chunk .getData (), algorithm );
273+
274+ // Chercher si ce chunk existe déjà en base
275+ Optional <ChunkEntity > existingChunk ;
276+ switch (algorithm ) {
277+ case SHA1 :
278+ existingChunk = chunkRepository .findByHashSha1 (hash );
279+ break ;
280+ case SHA256 :
281+ existingChunk = chunkRepository .findByHashSha256 (hash );
282+ break ;
283+ case BLAKE3 :
284+ existingChunk = chunkRepository .findByHashBlake3 (hash );
285+ break ;
286+ default :
287+ existingChunk = Optional .empty ();
288+ }
289+
290+ ChunkEntity chunkEntity ;
291+ if (existingChunk .isPresent ()) {
292+ chunkEntity = existingChunk .get ();
293+ duplicateChunks ++;
294+ savedStorage += chunk .getOriginalSize ();
295+ logger .info ("Chunk dupliqué trouvé: {}" , hash );
296+ } else {
297+ // Compression du chunk
298+ byte [] compressedData = compressionService .compress (chunk .getData (), compressionType );
299+ totalCompressedSize += compressedData .length ;
300+
301+ chunkEntity = new ChunkEntity ();
302+ // Stocker les données compressées
303+ chunkEntity .setData (compressedData );
304+ // Vous pouvez ajouter une propriété pour stocker la taille originale si besoin,
305+ // ex :
306+ // chunkEntity.setOriginalSize(chunk.getData().length);
307+
308+ // Stocker le hash selon l'algorithme
309+ switch (algorithm ) {
310+ case SHA1 :
311+ chunkEntity .setHashSha1 (hash );
312+ break ;
313+ case SHA256 :
314+ chunkEntity .setHashSha256 (hash );
315+ break ;
316+ case BLAKE3 :
317+ chunkEntity .setHashBlake3 (hash );
318+ break ;
319+ }
320+
321+ chunkEntity = chunkRepository .save (chunkEntity );
322+ uniqueChunks ++;
323+ }
324+
325+ // Créer la relation entre le fichier et le chunk
326+ FileChunkEntity fileChunk = new FileChunkEntity ();
327+ fileChunk .setFile (fileEntity );
328+ fileChunk .setChunk (chunkEntity );
329+ fileChunk .setPosition (chunk .getPosition ());
330+ fileChunkRepository .save (fileChunk );
331+ }
332+
333+ // 5. Préparer le résultat
334+ Map <String , Object > result = new HashMap <>();
335+ result .put ("fileId" , fileEntity .getId ());
336+ result .put ("fileName" , fileEntity .getName ());
337+ result .put ("extension" , fileEntity .getExtension ());
338+ result .put ("fileSize" , fileEntity .getSize ());
339+ result .put ("algorithm" , algorithm .name ());
340+ result .put ("compressionType" , compressionType .name ());
341+ result .put ("totalChunks" , totalChunks );
342+ result .put ("uniqueChunks" , uniqueChunks );
343+ result .put ("duplicateChunks" , duplicateChunks );
344+ result .put ("savedStorage" , savedStorage );
345+ result .put ("deduplicationRatio" , totalChunks > 0 ? (double ) duplicateChunks / totalChunks : 0 );
346+ result .put ("totalCompressedSize" , totalCompressedSize );
347+
348+ logger .info ("Fichier compressé traité: id={}, nom={}, chunks={}, uniques={}, doublons={}, taille compressée={}" ,
349+ fileEntity .getId (), fileName , totalChunks , uniqueChunks , duplicateChunks , totalCompressedSize );
350+
351+ return result ;
352+ }
353+ }
0 commit comments