【发布时间】:2019-08-02 19:27:47
【问题描述】:
在将此问题标记为重复之前,请阅读我写的内容。 我已经在很多页面中检查了许多问题以找到解决方案,但找不到任何东西。 在我当前的应用程序中,我正在使用这个:
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 秒才能获得文件 ID。
我想知道是否有任何其他方法可以使用或不使用散列或流从文件中获取独特的东西?我的目标机器一直不是 NTFS 或 windows,所以我必须找到另一种方法。
我想知道如果我只是从流中获取前“x”个字节并使用该减小大小的流对唯一 ID 进行哈希处理是否有意义?
编辑:这不是为了安全或其他任何事情,我需要这个唯一 ID,因为 FileSystemWatcher 不工作:)
EDIT2:基于 cmets,我决定更新我的问题。我这样做的原因可能不是基于为文件创建唯一 ID 的解决方案。 我的问题是我必须观看文件夹并在有事件时触发事件; A) 新添加的文件 B) 更改的文件 C) 删除的文件
我不能使用 FileSystemWatcher 的原因是它不可靠。有时我将 100x 文件放入文件夹,而 FileSystemWatcher 仅触发 20x-30x 事件,如果它是网络驱动器,有时它可能会更低。 我的方法是将所有文件及其唯一 ID 保存到一个文本文件中,如果有任何更改,则每 5 秒检查一次索引文件。如果没有像 18GB 这样的大文件,它工作正常。但是计算 40GB 文件的哈希值太长了。 我的问题是:当我正在观看的文件夹发生问题时,如何触发事件
EDIT3:设置赏金后,我意识到我需要提供更多关于我的代码中发生了什么的信息。首先,这是我对用户@JustShadow 的回答(太长了,我无法将其作为评论发送) 我将解释我是如何做到的,我将 filepath-uniqueID(MD5 hashed) 保存在文本文件中,每 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如果我的列表的两个计数都大于零,因此会触发其他情况。在 else 里面我已经知道 a 的散列值(它在我 5 秒前创建的文本文件中)现在我将它与所有 AdditionalList 元素进行比较,看看如果我得到匹配,我是否可以匹配它们,如果没有,那就是重命名情况匹配然后我可以说 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's 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 ----------");
}
}
【问题讨论】:
-
评论不用于扩展讨论;这个对话是moved to chat。
-
@shino-lex,我在下面更新了我的答案。请再检查一遍。
-
您在使用
FileSystemWatcher时是否尝试关注these recommendations? -
@Spotted 是的,我已经在 FileSystemWatcher 端尝试了所有方法,但仍然相同,我收到此错误:目录中一次更改太多:xxx 即使我将 FSW 设置为仅捕获创建(甚至没有更改或任何东西)否则)具有最大缓冲区的事件。我只是粘贴了 1 个小 txt 文件进行测试(7-8kb),但仍然出现此错误
-
可以重命名文件(在您的应用程序之外)吗?文件可以在一秒钟内更改多次吗?我正在考虑结合文件名和上次修改日期来检测更改。如果满足先决条件,我将发布一些涉及此的代码。
标签: c#