gpt4 book ai didi

c# - 比较具有相同内容的 XLSX 文件之间的 MD5 哈希值

转载 作者:太空宇宙 更新时间:2023-11-03 12:36:15 25 4
gpt4 key购买 nike

我们有一个内部网络应用程序,它接受来自用户的各种格式的文件,以便将大量数据导入我们的系统。

我们最近实现的升级之一是添加一种方法来检测文件是否以前上传过,如果是,则向用户发出警告并提供重新提交文件或取消上传的选项.

为此,我们正在计算上传文件的 MD5,并将其与包含先前上传文件信息的数据库表进行比较,以确定它是否重复。如果 MD5 匹配,则显示警告,否则将新文件信息插入表中并继续文件处理。

以下是用于生成MD5哈希的C#代码:

private static string GetHash(byte[] input)
{
using (MD5 md5 = MD5.Create())
{
byte[] data = md5.ComputeHash(input);

StringBuilder bob = new StringBuilder();

for (int i = 0; i < data.Length; i++)
bob.Append(data[i].ToString("x2").ToUpper());

return bob.ToString();
}
}

一切都运行良好...只有一个异常(exception)。

用户可以为此过程上传 .xlsx 文件,不幸的是,这种文件类型还在文件内容中存储了文件的元数据。 (通过将 .xlsx 文件的扩展名更改为 .zip 并提取内容 [见下文],可以很容易地看到这一点。)

Excel Metadata

因此,.xlsx 文件的 MD5 散列将随着每次后续保存而改变,即使文件内容相同(只是打开和保存没有修改的文件将刷新元数据并导致不同的 MD5 哈希)。

在这种情况下,具有相同记录但在不同时间或由不同用户创建的文件将绕过重复文件检测并得到处理。

我的问题:有没有一种方法可以确定.xlsx 文件的内容 是否与上一个文件的 不存储文件内容?换句话说:有没有办法生成 .xlsx 文件的 内容MD5 散列?

最佳答案

在计算哈希值之前,您可以从文档中删除不应影响哈希值的部分。

这可以通过将 Open XML 包的所有部分提取到单个 XML 文档、删除不需要的节点并计算生成的 XML 文档的散列来实现。请注意,您必须重新计算已上传的 Excel 文件的哈希值,因为哈希值现在不再基于二进制文件内容。

这是一个简单的示例程序(添加对 WindowsBase.dll 的引用):

using System;
using System.IO;
using System.IO.Packaging;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Xml.Linq;

internal class Program
{
private static readonly XNamespace dcterms = "http://purl.org/dc/terms/";

private static void Main(string[] args)
{
var fileName = args[0];

// open the ZIP package
var package = Package.Open(fileName);

// convert the package to a single XML document
var xdoc = OpcToFlatOpc(package);

// remove the nodes we are not interested in
// here you can add other nodes as well
xdoc.Descendants(dcterms + "modified").Remove();

// get a stream of the XML and compute the hash
using (var ms = new MemoryStream())
{
xdoc.Save(ms);
ms.Position = 0;

string md5 = GetHash(ms);
Console.WriteLine(md5);
}
}

private static string GetHash(Stream stream)
{
using (var md5 = MD5.Create())
{
var data = md5.ComputeHash(stream);

var bob = new StringBuilder();

for (int i = 0; i < data.Length; i++)
{
bob.Append(data[i].ToString("X2"));
}

return bob.ToString();
}
}

private static XDocument OpcToFlatOpc(Package package)
{
XNamespace pkg = "http://schemas.microsoft.com/office/2006/xmlPackage";
var declaration = new XDeclaration("1.0", "UTF-8", "yes");
var doc = new XDocument(
declaration,
new XProcessingInstruction("mso-application", "progid=\"Word.Document\""),
new XElement(
pkg + "package",
new XAttribute(XNamespace.Xmlns + "pkg", pkg.ToString()),
package.GetParts().Select(GetContentsAsXml)));

return doc;
}

private static XElement GetContentsAsXml(PackagePart part)
{
XNamespace pkg = "http://schemas.microsoft.com/office/2006/xmlPackage";
if (part.ContentType.EndsWith("xml"))
{
using (var partstream = part.GetStream())
{
using (var streamReader = new StreamReader(partstream))
{
string streamString = streamReader.ReadToEnd();
if (!string.IsNullOrEmpty(streamString))
{
var newXElement =
new XElement(
pkg + "part",
new XAttribute(pkg + "name", part.Uri),
new XAttribute(pkg + "contentType", part.ContentType),
new XElement(pkg
+ "xmlData", XElement.Parse(streamString)));
return newXElement;
}

return null;
}
}
}

using (var str = part.GetStream())
{
using (var binaryReader = new BinaryReader(str))
{
int len = (int)binaryReader.BaseStream.Length;
var byteArray = binaryReader.ReadBytes(len);

// the following expression creates the base64String, then chunks
// it to lines of 76 characters long
string base64String = Convert.ToBase64String(byteArray)
.Select((c, i) => new { Character = c, Chunk = i / 76 })
.GroupBy(c => c.Chunk)
.Aggregate(
new StringBuilder(),
(s, i) =>
s.Append(
i.Aggregate(
new StringBuilder(),
(seed, it) => seed.Append(it.Character),
sb => sb.ToString()))
.Append(Environment.NewLine),
s => s.ToString());

return new XElement(
pkg + "part",
new XAttribute(pkg + "name", part.Uri),
new XAttribute(pkg + "contentType", part.ContentType),
new XAttribute(pkg + "compression", "store"),
new XElement(pkg + "binaryData", base64String));
}
}
}
}

关于c# - 比较具有相同内容的 XLSX 文件之间的 MD5 哈希值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40892409/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com