Прежде чем пометить этот вопрос как дубликат, пожалуйста, прочитайте то, что я пишу. Я проверил много вопросов на многих страницах для решения, но не смог ничего найти. В моем текущем приложении я использовал это:
using (var md5 = MD5.Create())
{
using (FileStream stream = File.OpenRead(FilePath))
{
var hash = md5.ComputeHash(stream);
var cc = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
Console.WriteLine("Unique ID : " + cc);
}
}
Это работало достаточно хорошо для файлов небольшого размера, но как только я попробовал это для файлов большого размера, мне понадобилось около 30-60 секунд, чтобы получить идентификатор файла.
Интересно, есть ли другой способ получить что-то уникальное из файла с использованием хеширования или потока или без него? Моя целевая машина не NTFS или Windows все время, поэтому я должен найти другой путь.
Мне было интересно, имеет ли смысл, если я просто получу первое "х" количество байтов из потока и выполню хеширование для уникального идентификатора с этим потоком уменьшенного размера?
РЕДАКТИРОВАТЬ: Это не для безопасности или чего-то еще, мне нужен этот уникальный идентификатор, потому что FileSystemWatcher не работает :)
РЕДАКТИРОВАТЬ 2: На основе комментариев я решил обновить свой вопрос. Причина, по которой я это делаю, может быть, есть решение, которое не основано на создании уникального идентификатора для файла. Моя проблема в том, что я должен наблюдать за папкой и запускать события, когда они есть; A) Недавно добавленные файлы B) Измененные файлы C) Удаленные файлы
Причина, по которой я не могу использовать FileSystemWatcher, заключается в том, что она не надежна. Иногда я помещаю файл 100x в папку, а FileSystemWatcher запускает только события 20x-30x, и если это сетевой диск, иногда он может быть ниже. Мой метод состоял в сохранении всех файлов и их уникального идентификатора в текстовый файл и проверял индексный файл каждые 5 секунд, если есть какие-либо изменения. Если нет больших файлов, таких как 18 ГБ, он работает нормально.. Но вычисление хеш файла 40 ГБ занимает слишком много времени. Мой вопрос: как я могу запускать события, когда что-то происходит с папкой, которую я наблюдаю
РЕДАКТИРОВАТЬ 3: После установки награды я понял, что мне нужно дать больше информации о том, что происходит в моем коде. Сначала это мой ответ пользователю @JustShadow (это было слишком долго, поэтому я не мог отправить его в качестве комментария). Я объясню, как я это делаю, я сохраняю filepath-uniqueID (MD5-хэш) в текстовом файле и каждые 5 секунд проверяю папка с Directory.GetFiles(DirectoryPath); Затем я сравниваю свой первый список со списком, который у меня был 5 секунд назад, и таким образом я получаю 2 списка
List<string> AddedList = FilesInFolder.Where(x => !OldList.Contains(x)).ToList();
List<string> RemovedList = OldList.Where(x => !FilesInFolder.Contains(x)).ToList();
Вот как я их получаю. Теперь у меня есть свои блоки if,
if (AddedList.Count > 0 && RemovedList.Count == 0)
то это хорошо, что не переименовывает только новые файлы. Я хэширую все новые файлы и добавляю их в свой текстовый файл.
if (AddedList.Count == 0 && RemovedList.Count > 0)
Напротив первого, если все еще хорошо, есть только удаленный элемент, я удаляю их из текстового файла на этом и все готово. После этой ситуации наступает мой блок else. Здесь я и сравниваю, в основном я хэширую все добавленные и удаленные элементы списка, а затем беру те, которые существуют в обоих списках, как, например, a.txt, переименованный в b.txt в этом case оба из моего списка счетчика будет больше нуля, так что в противном случае срабатывает. Внутри остального я уже знаю хешированное значение (оно внутри моего текстового файла, которое я создал 5 секунд назад), теперь я сравниваю его со всеми элементами AddedList и смотрю, могу ли я сопоставить их, если получу совпадение, тогда это ситуация переименования, если нет тогда я могу сказать, что b.txt действительно недавно добавлен в список со времени последнего сканирования. Я также предоставлю часть моего кода класса, так что, возможно, есть способ решить эту загадку.
Теперь я также поделюсь некоторыми из моего кода класса, может быть, мы сможем найти способ решить его, когда все будут знать, что я на самом деле делаю. Вот так выглядит мой таймер
private void TestTmr_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
lock (locker)
{
if (string.IsNullOrWhiteSpace(FilePath))
{
Console.WriteLine("Timer will be return because FilePath is empty. --> " + FilePath);
return;
}
try
{
if (!File.Exists(FilePath + @"\index.MyIndexFile"))
{
Console.WriteLine("File not forund. Will be created now.");
FileStream close = File.Create(FilePath + @"\index.MyIndexFile");
close.Close();
return;
}
string EncryptedText = File.ReadAllText(FilePath + @"\index.MyIndexFile");
string JsonString = EncClass.Decrypt(EncryptedText, "SecretPassword");
CheckerModel obj = Newtonsoft.Json.JsonConvert.DeserializeObject<CheckerModel>(JsonString);
if (obj == null)
{
CheckerModel check = new CheckerModel();
FileInfo FI = new FileInfo(FilePath);
check.LastCheckTime = FI.LastAccessTime.ToString();
string JsonValue = Newtonsoft.Json.JsonConvert.SerializeObject(check);
if (!File.Exists(FilePath + @"\index.MyIndexFile"))
{
FileStream GG = File.Create(FilePath + @"\index.MyIndexFile");
GG.Close();
}
File.WriteAllText(FilePath + @"\index.MyIndexFile", EncClass.Encrypt(JsonValue, "SecretPassword"));
Console.WriteLine("DATA FILLED TO TEXT FILE");
obj = Newtonsoft.Json.JsonConvert.DeserializeObject<CheckerModel>(JsonValue);
}
DateTime LastAccess = Directory.GetLastAccessTime(FilePath);
string[] FilesInFolder = Directory.GetFiles(FilePath, "*.*", SearchOption.AllDirectories);
List<string> OldList = new List<string>(obj.Files.Select(z => z.Path).ToList());
List<string> AddedList = FilesInFolder.Where(x => !OldList.Contains(x)).ToList();
List<string> RemovedList = OldList.Where(x => !FilesInFolder.Contains(x)).ToList();
if (AddedList.Count == 0 & RemovedList.Count == 0)
{
//no changes.
Console.WriteLine("Nothing changed since last scan..!");
}
else if (AddedList.Count > 0 && RemovedList.Count == 0)
{
Console.WriteLine("Adding..");
//Files added but removedlist is empty which means they are not renamed. Fresh added..
List<System.Windows.Forms.ListViewItem> LvItems = new List<System.Windows.Forms.ListViewItem>();
for (int i = 0; i < AddedList.Count; i++)
{
LvItems.Add(new System.Windows.Forms.ListViewItem(AddedList[i] + " has added since last scan.."));
FileModel FileItem = new FileModel();
using (var md5 = MD5.Create())
{
using (FileStream stream = File.OpenRead(AddedList[i]))
{
FileItem.Size = stream.Length.ToString();
var hash = md5.ComputeHash(stream);
FileItem.Id = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
}
FileItem.Name = Path.GetFileName(AddedList[i]);
FileItem.Path = AddedList[i];
obj.Files.Add(FileItem);
}
}
else if (AddedList.Count == 0 && RemovedList.Count > 0)
{
//Files removed and non has added which means files have deleted only. Not renamed.
for (int i = 0; i < RemovedList.Count; i++)
{
Console.WriteLine(RemovedList[i] + " has been removed from list since last scan..");
obj.Files.RemoveAll(x => x.Path == RemovedList[i]);
}
}
else
{
//Check for rename situations..
//Scan newly added files for MD5 ID's. If they are same with old one that means they are renamed.
//if a newly added file has a different MD5 ID that is not represented in old ones this file is fresh added.
for (int i = 0; i < AddedList.Count; i++)
{
string NewFileID = string.Empty;
string NewFileSize = string.Empty;
using (var md5 = MD5.Create())
{
using (FileStream stream = File.OpenRead(AddedList[i]))
{
NewFileSize = stream.Length.ToString();
var hash = md5.ComputeHash(stream);
NewFileID = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
}
FileModel Result = obj.Files.FirstOrDefault(x => x.Id == NewFileID);
if (Result == null)
{
//Not a rename. It fresh file.
Console.WriteLine(AddedList[i] + " has added since last scan..");
//Scan new file and add it to the json list.
}
else
{
Console.WriteLine(Result.Path + " has renamed into --> " + AddedList[i]);
//if file is replaced then it should be removed from RemovedList
RemovedList.RemoveAll(x => x == Result.Path);
obj.Files.Remove(Result);
//After removing old one add new one. This way new one will look like its renamed
FileModel ModelToadd = new FileModel();
ModelToadd.Id = NewFileID;
ModelToadd.Name = Path.GetFileName(AddedList[i]);
ModelToadd.Path = AddedList[i];
ModelToadd.Size = NewFileSize;
obj.Files.Add(ModelToadd);
}
}
//After handle AddedList we should also inform user for removed files
for (int i = 0; i < RemovedList.Count; i++)
{
Console.WriteLine(RemovedList[i] + " has deleted since last scan.");
}
}
//Update Json after checking everything.
obj.LastCheckTime = LastAccess.ToString();
File.WriteAllText(FilePath + @"\index.MyIndexFile", EncClass.Encrypt(Newtonsoft.Json.JsonConvert.SerializeObject(obj), "SecretPassword"));
}
catch (Exception ex)
{
Console.WriteLine("ERROR : " + ex.Message);
Console.WriteLine("Error occured --> " + ex.Message);
}
Console.WriteLine("----------- END OF SCAN ----------");
}
}