Duplicate File Locator 1.0
Loading...
Searching...
No Matches
DuplicateFileLocator.cs
Go to the documentation of this file.
3using Newtonsoft.Json;
4using System.Drawing;
5using System.Security.Cryptography;
6using System.Text;
7
9{
11 {
12 #region Private Attributes
13
14 private const string DUPLICATED_FILES_JSON = "C:\\Users\\Alexa\\repos\\duplicate-file-locator\\duplicated-images.json";
15 private const string DEFAULT_TXT_OUTPUT_PATH = "C:\\Users\\Alexa\\repos\\duplicate-file-locator\\duplicated-images.txt";
16
17 // This is used to replace DUPLICATED_FILES_JSON
18 private string _duplicatedFilesJson;
19
20 private List<IDuplicatedFile> _duplicatedFiles;
21
22 #endregion
23
24 #region Constructor
25
30 {
31 _duplicatedFiles = new List<IDuplicatedFile>();
32
33 LoadData();
34 }
35
36 public DuplicateFileLocator(string jsonPath)
37 {
38 _duplicatedFilesJson = jsonPath;
39
40 _duplicatedFiles = new List<IDuplicatedFile>();
41
42 LoadData();
43 }
44
45 #endregion
46
47 #region Public Methods
48
49 public void FindDuplicateFiles(string dir)
50 {
51 // Check if path exists.
52 if (Path.Exists(dir))
53 {
54 // List contains all file paths but by the end of the method will only contatin
55 // non duplicate file paths (paths to files which are not duplicates)
56 List<string> filePaths = GetAllFilesInDirectory(dir);
57 int totalFiles = filePaths.Count;
58
59 // Check folder contains files
60 if (totalFiles > 0)
61 {
62 int filesHashed = 0;
63 // hashesFound stores all the different hashes calculated.
64 List<string> hashesFound = new List<string>();
65
66 for (int i = 0; i < filePaths.Count; i++)
67 {
68 // Calculate and display progress
69 int progress = (filesHashed * 100) / totalFiles;
70 ConsoleUtility.WriteProgressBar(progress, true);
71
72 string hash = CreateHash(filePaths[i]);
73 filesHashed++;
74
75 if (CheckHash(hash, hashesFound))
76 {
77 AddDuplicateFile(hash, filePaths[i]);
78 // Remove the duplicate path from list
79 filePaths.Remove(filePaths[i]);
80 i--;
81 }
82 else
83 {
84 // As this hash has not beed calculated yet, add to list.
85 hashesFound.Add(hash);
86 }
87 }
88
89 ConsoleUtility.WriteProgressBar(100, true);
90 Console.WriteLine("\nTotal Images checked : {0}\nCollating Data now...\n", filesHashed);
91
92 // When the duplicate files are added only the hash and duplicate path is known.
93 // Using the filesPaths and hashesFound lists you can locate the original file path
94 // because each index is related.
95 // filePaths only contains paths of files which aren't duplicates and the original paths of the duplicates.
96 // hashesFound only contains hashes never found before at the time of creation of the hash
97 FindOriginalPaths(filePaths, hashesFound);
98
99 SaveData();
100 }
101 else
102 {
103 Console.WriteLine("There are no files in this directory and it's subdirectories, please try again.\n");
104 }
105
106 }
107 else
108 {
109 Console.WriteLine("Path does not exist, please try again.\n");
110 }
111
112 }
113
115 {
116 Console.WriteLine("Verifying duplicate files found...\n");
117
118 // List of paths that are not duplicates
119 List<string> notDuplicatePaths = new List<string>();
120
121 for (int i = 0; i < _duplicatedFiles.Count; i++)
122 {
123 string ogPath = _duplicatedFiles[i].OriginalPath;
124 Bitmap img1 = new Bitmap(ogPath);
125 foreach (var duplicatedPath in _duplicatedFiles[i].DuplicatePaths)
126 {
127 Bitmap img2 = new Bitmap(duplicatedPath);
128
129 if (!AreImagesEqual(img1, img2))
130 {
131 // Images are not duplicates.
132 notDuplicatePaths.Add(duplicatedPath);
133 }
134 }
135
136 // If the "duplicate files" were not actually duplicates
137 // If there is just one added, then no other duplicates
138 if (notDuplicatePaths.Count > 1)
139 {
140 DuplicatedFile temp = new DuplicatedFile(_duplicatedFiles[i].Hash);
141 temp.OriginalPath = notDuplicatePaths[0];
142 notDuplicatePaths.RemoveAt(0);
143 foreach (var path in notDuplicatePaths)
144 {
145 temp.AddDuplicatePath(path);
146 }
147
148 _duplicatedFiles.Add(temp);
149 // Clear array otherwise you'll keep adding to it for the new file.
150 // Only file paths with the same hash should be stored in the array
151 // at a given time.
152 notDuplicatePaths = new List<string>();
153 }
154 }
155 }
156
158 {
159 Console.WriteLine(ToString());
160 }
161
163 {
164 if (_duplicatedFilesJson != null)
165 {
166 using (StreamWriter sw = File.CreateText(_duplicatedFilesJson))
167 {
168 sw.WriteLine();
169 }
170 }
171 else
172 {
173 using (StreamWriter sw = File.CreateText(DUPLICATED_FILES_JSON))
174 {
175 sw.WriteLine();
176 }
177 }
178
179 Console.WriteLine("Duplicate file cleared.\n");
180 }
181
183 {
184 using (StreamWriter sw = File.CreateText(DEFAULT_TXT_OUTPUT_PATH))
185 {
186 sw.WriteLine(ToString());
187 }
188 Console.WriteLine("Duplicate file exported.\n");
189 }
190
191 public void ExportDuplicateFiles(string filePath)
192 {
193 using (StreamWriter sw = File.CreateText(filePath))
194 {
195 sw.WriteLine(ToString());
196 }
197 Console.WriteLine("Duplicate file exported.\n");
198 }
199
200 public void HashIndividualFile(string filePath)
201 {
202 if (Path.Exists(filePath))
203 {
204 string hash = CreateHash(filePath);
205 Console.WriteLine("Hash : {0}\n", hash);
206 }
207 else
208 {
209 Console.WriteLine("Path does not exist, please try again.\n");
210 }
211 }
212
213 #endregion
214
215 #region Private Methods
216
226 private List<string> GetAllFilesInDirectory(string dir)
227 {
228 List<string> files = new List<string>();
229 if (Directory.Exists(dir))
230 {
231 List<string> subdirs = Directory.GetDirectories(dir).ToList<string>();
232 foreach (string subdir in subdirs)
233 {
234 files.AddRange(GetAllFilesInDirectory(subdir));
235 }
236
237 // Add the files in the current directory
238 files.AddRange(Directory.GetFiles(dir).ToList<string>());
239 }
240 return files;
241 }
242
252 private string ByteArrayToString(byte[] arrInput)
253 {
254 int i;
255 StringBuilder sOutput = new StringBuilder(arrInput.Length);
256 for (i = 0; i < arrInput.Length; i++)
257 {
258 sOutput.Append(arrInput[i].ToString("X2"));
259 }
260 return sOutput.ToString();
261 }
262
272 private string CreateHash(string filePath)
273 {
274 try
275 {
276 Bitmap img = new Bitmap(filePath);
277
278 byte[] tmpSource;
279 byte[] tmpHash;
280
281 // Create a byte array from source data.
282 MemoryStream stream = new MemoryStream();
283 img.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
284 tmpSource = stream.ToArray();
285
286 // Compute hash based on source data.
287 tmpHash = new MD5CryptoServiceProvider().ComputeHash(tmpSource);
288
289 return ByteArrayToString(tmpHash);
290 }
291 catch
292 {
293 // TODO Add error.
294 return string.Empty;
295 }
296 }
297
307 private void AddDuplicateFile(string hash, string filePath)
308 {
309 if (_duplicatedFiles.Any(file => file.Hash == hash))
310 {
311 // Get the DuplicatedFile object with the same hash value and add duplicate path.
312 IDuplicatedFile duplicateFile = _duplicatedFiles.FirstOrDefault(file => file.Hash == hash);
313
314 // Only adds file path if it's different to what's already be found.
315 if (duplicateFile.OriginalPath != filePath && !duplicateFile.DuplicatePaths.Contains(filePath))
316 {
317 duplicateFile.AddDuplicatePath(filePath);
318 }
319
320 }
321 else
322 {
323 // Create a new object with the hash and duplicate path.
324 _duplicatedFiles.Add(new DuplicatedFile(hash, filePath));
325 }
326 }
327
338 private void FindOriginalPaths(List<string> filePaths, List<string> hashesFound)
339 {
340 if (filePaths.Count == hashesFound.Count)
341 {
342 foreach (var file in _duplicatedFiles)
343 {
344 // only find original path if the duplicate file object doesn't have one
345 if (file.OriginalPath == string.Empty)
346 {
347 int i = hashesFound.IndexOf(file.Hash);
348
349 // Only add the ogPath if hash exists
350 if (i >= 0)
351 {
352 string ogPath = filePaths[i];
353 file.OriginalPath = ogPath;
354 }
355 }
356
357 }
358 }
359 else
360 {
361 Console.WriteLine("Note: Can't find original files as filePaths and hashesFound are different in length.\n");
362 }
363 }
364
368 private void LoadData()
369 {
370 string json = string.Empty;
371 if (_duplicatedFilesJson != null)
372 {
373 json = File.ReadAllText(_duplicatedFilesJson);
374 }
375 else
376 {
377 json = File.ReadAllText(DUPLICATED_FILES_JSON);
378 }
379
380 try
381 {
382 var deserialized = JsonConvert.DeserializeObject<List<DuplicatedFile>>(json);
383 _duplicatedFiles = deserialized.Cast<IDuplicatedFile>().ToList();
384 }
385 catch
386 {
387
388 }
389 }
390
394 private void SaveData()
395 {
396 string json = JsonConvert.SerializeObject(_duplicatedFiles);
397
398 if (_duplicatedFilesJson != null)
399 {
400 File.WriteAllText(_duplicatedFilesJson, json);
401 }
402 else
403 {
404 File.WriteAllText(DUPLICATED_FILES_JSON, json);
405 }
406 }
407
412 private string ToString()
413 {
414 string output = String.Empty;
415 foreach (var file in _duplicatedFiles)
416 {
417 output += file + "\n";
418 }
419
420 if (output == String.Empty)
421 {
422 output = "No duplicate files found.";
423 }
424
425 return output;
426 }
427
440 private bool AreImagesEqual(Bitmap bmp1, Bitmap bmp2)
441 {
442 if (bmp1.Width != bmp2.Width || bmp1.Height != bmp2.Height)
443 return false;
444
445 for (int y = 0; y < bmp1.Height; y++)
446 {
447 for (int x = 0; x < bmp1.Width; x++)
448 {
449 if (bmp1.GetPixel(x, y) != bmp2.GetPixel(x, y))
450 return false;
451 }
452 }
453
454 return true;
455 }
456
463 private bool CheckHash(string hash, List<string> hashesFound)
464 {
465 if (hash == string.Empty)
466 return false;
467 else
468 // returns true if hash is already a known duplicate or if the hash is contained within
469 // the temporary list of known hashes.
470 return _duplicatedFiles.Any(file => file.Hash == hash) || hashesFound.Contains(hash);
471 }
472
473 #endregion
474 }
475}
void HashIndividualFile(string filePath)
Method hashes an individual file.
void ExportDuplicateFiles(string filePath)
Method exports the duplicate data in the same format as DisplayDuplicateFiles.
void ExportDuplicateFiles()
Method exports the duplicate data in the same format as DisplayDuplicateFiles.
void ClearDuplicateFiles()
Method deletes all the data relating to duplicate files found.
void FindDuplicateFiles(string dir)
Method searches recursively to find duplicated files.
void VerifyDuplicateFiles()
Method verifies that the files are in fact duplicates. This is needed as different files might create...
DuplicateFileLocator()
Method initalises attributes and loads data.
void DisplayDuplicateFiles()
Methods displays the duplicate files found including the original path.
List< string > DuplicatePaths
List of paths to duplicate files.
void AddDuplicatePath(string path)
Method adds path to DuplicatePaths.