使用LINQ查找目录中的重复文件
本文关键字:文件 LINQ 查找 使用 | 更新日期: 2023-09-27 18:01:15
我目前正在编写一个程序,该程序使用用户给定的参数从各种来源大规模下载图像。
我的问题是我不希望重复发生。我应该指出,我正在处理一次最多100次的大规模下载(不是那么大规模(,每个文件都有不同的名称,所以简单地按文件名搜索是行不通的,我需要检查哈希。
无论如何,以下是我已经发现的:
Directory.GetFiles(FullPath)
.Select(f => new
{
FileName = f,
FileHash = Encoding.UTF8.GetString(new SHA1Managed().ComputeHash(new FileStream(f, FileMode.Open, FileAccess.Read)))
})
.GroupBy(f => f.FileHash)
.Select(g => new { FileHash = g.Key, Files = g.Select(z => z.FileName).ToList() })
.SelectMany(f => f.Files.Skip(1))
.ToList()
.ForEach(File.Delete);
我的问题是,在"File.Delete"行中,我得到了一个非常著名的错误,即该文件已经被另一个进程使用。我认为这是因为上面的代码在删除文件之前缺乏关闭用于获取FileHash的FileStream的方法,但我不知道如何解决这个问题,有什么想法吗?
我还应该指出,我已经尝试过其他解决方案,比如这个(没有linq(:https://www.bhalash.com/archives/13544802709用删除功能替换打印功能,没有错误,但不起作用。
提前感谢,我随时可以获得所需的任何其他信息!:(
Akitake
您忘记了处理FileStream
,所以在GC收集对象之前,该文件仍处于打开状态。
您可以将Select
子句替换为:
.Select(f => {
using (var fs = new FileStream(f, FileMode.Open, FileAccess.Read))
{
return new
{
FileName = f,
FileHash = BitConverter.ToString(SHA1.Create().ComputeHash(fs))
});
}
})
请勿使用Encoding.UTF8
对任意字节(哈希就是这样(进行编码,因为结果可能是无效的UTF8序列。如果必须使用BitConverter.ToString
,或者更好:找到一种不涉及字符串的不同方法。
例如,你可以写:
.Select(f => {
// Same as above, but with:
// FileHash = SHA1.Create().ComputeHash(fs)
})
.GroupBy(f => f.FileHash, StructuralComparisons.StructuralEqualityComparer)
不过,您可以使用更好的方法:您可以先按大小对文件进行分组,如果有多个大小相同的文件,则仅计算哈希。当没有太多重复时,它应该会表现得更好。
为了解决干净地处理文件流的问题,可以将文件哈希的计算拆分为如下方法:
static string GetHash(string path)
{
using (var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read))
{
return Encoding.UTF8.GetString(new SHA1Managed().ComputeHash(fileStream));
}
}
并像这样消费:
Directory.GetFiles(FullPath)
.Select(
f => new
{
FileName = f,
FileHash = GetHash(f)
})
.GroupBy(f => f.FileHash)
.Select(g => new { FileHash = g.Key, Files = g.Select(z => z.FileName).ToList() })
.SelectMany(f => f.Files.Skip(1))
.ToList()
.ForEach(File.Delete);