我有一个方法,它采用带有
IFormFile
扩展名的 .zip
并读取压缩在其中的文件。它工作得很好,但是,在某些情况下,符号æ, å, ø
的编码是错误的,它们被表示为问号。这种情况并不是每次都会发生,我在这里举一些例子。
这就是代码的样子
var stream = file.OpenReadStream(); // This here is the stream from the IFormFile
var archive = new ZipArchive(stream);
foreach (var entry in archive.Entries)
{
// Do something with the entries
}
当问题出现时,
entry.Name
可能看起来像这样Tilbud 2 - Det r�de hus.pdf
。
我尝试过向
ZipArchive
构造函数提供 props
var archive = new ZipArchive(stream, ZipArchiveMode.Read, false, Encoding.ASCII);
当编码为
ASCII
时,文件名如下 - Tilbud 2 - Det r?de hus.pdf
。
当我尝试
Encoding.Unicode
时,损坏的符号取问号的值(不确定我给出的示例是否是实际数字) - Tilbud 2 - Det r\u300de hus.pdf
前端只是axios post请求。我的想法是,某些 zip 在到达后端时具有不同的编码,这就是为什么有时这些符号可以工作的原因。
我还尝试在调用
IFormFile
之前将 ZipArchive
流的编码更改为 UTF-8,但这并不是那么简单,所以我首先想找出这是否确实是问题所在。
请随意提出问题并针对奇怪的行为提出建议。
不适用于 net6
ZipArchive
类,所以这不是直接答案。但根据我之前使用 zip 存档的经验,您实际上可以获得存档内文件名的任何编码,包括非常奇特的编码。据我所知,存档中没有标准位置可以存储确切的编码以供将来在解压缩期间使用。同时,为了成功解压缩,您应该使用与压缩期间使用的编码完全相同的编码。
就个人而言,我最终使用
chardetsharp
(https://github.com/superstrom/chardetsharp)进行启发式编码检测。这显着提高了解包的成功率,但并没有完全解决问题(即,当编码预测错误时,我仍然有一些存在解压错误或文件名不正确的档案)。
SharpCompress
库的示例用法(不知道如何将其集成到BCL中的ZipArchive
中):
var arch = ArchiveFactory.Open(path,
new SharpCompress.Readers.ReaderOptions
{
LookForHeader = true,
ArchiveEncoding = new SharpCompress.Common.ArchiveEncoding
{
CustomDecoder = CustomDecoder
}
});
arch.WriteToDirectory(targetDir, new SharpCompress.Common.ExtractionOptions
{
ExtractFullPath=true,
});
private static string CustomDecoder(byte[] arg1, int arg2, int arg3)
{
var bytesToRead = arg1.Skip(arg2).Take(arg3).ToArray();
if (bytesToRead.Length == 0)
{
return null;
}
var det = new Mozilla.CharDet.UniversalDetector();
det.HandleData(bytesToRead);
det.DataEnd();
var enc = Encoding.GetEncoding(det.DetectedCharsetName ?? Encoding.UTF8.BodyName);
return enc.GetString(bytesToRead);
}
看起来我使用的
chardetsharp
实现没有 nuget 包。这对于我的特定任务来说不是问题,我只是克隆了 chardetsharp
源并将 \src\CharDet\CharDet.csproj
添加到解决方案中,然后将其用作 ProjectReference
。然后我在我的 chardetsharp
中调用 CustomDecoder
,如上所示。
在生产中,您可以尝试使用其他编码检测库,例如
NChardet
(https://github.com/thinksea/NChardet),它也是原始 Mozilla 库的 C# 端口,因此它应该具有一个类似的API(但我没有使用过,所以我不确定)并且有一个nuget包。