运行环境
- 后端框架:
ASP.NET Core 5.0
NPOI
:2.5.3
问题描述
使用前端导入 Excel
的 .xlsx
类型文件时,后端获取到文件流 Stream
后,通过NPOI转换 Stream
为 XSSFWorkbook
类型时抛出异常 ICSharpCode.SharpZipLib.Zip.ZipException:“Wrong Local header signature: 0x65231462”
根本原因
Excel
被系统加密,导致代码无法正常读取
解决方案
将对应 Excel
解密即可
问题追踪
- 通过
VS
进行断点调试,调用堆栈如下ICSharpCode.SharpZipLib.Zip.ZipInputStream.GetNextEntry() NPOI.OpenXml4Net.Util.ZipInputStreamZipEntrySource.ZipInputStreamZipEntrySource(ICSharpCode.SharpZipLib.Zip.ZipInputStream) NPOI.OpenXml4Net.OPC.ZipPackage.ZipPackage(System.IO.Stream, NPOI.OpenXml4Net.OPC.PackageAccess) NPOI.OpenXml4Net.OPC.OPCPackage.Open(System.IO.Stream) NPOI.Util.PackageHelper.Open(System.IO.Stream) NPOI.XSSF.UserModel.XSSFWorkbook.XSSFWorkbook(System.IO.Stream)
- 抛出异常的地方函数为
ZipInputStream.GetNextEntry()
,其具体代码大致如下public ZipEntry GetNextEntry() { if (crc == null) { throw new InvalidOperationException("Closed."); } if (entry != null) { CloseEntry(); } int num = inputBuffer.ReadLeInt(); //略 if (num != 67324752) { throw new ZipException("Wrong Local header signature: 0x" + $"{num:X}"); } //略 return entry; }
ReadLeInt()
方法通过从inputBuffer
中读取int
类型,获得数字num
,即为异常中提示的Local header signature
,当local header signature
不为十进制数字67324752(即十六进制数字0x4034B50
时),系统判定其有异常并抛出。 - 什么是 Local header signature ?
局部文件头 (Local File Header
) 是Zip
文件结构中用于描述单个文件元数据的部分,位于文件开头,包含文件的通用标志、压缩方式、时间戳等信息,其具体结构可以查看规范文档
而局部文件头签名 (Local file header signature
) 是局部文件头中一个4字节的字段,用于标识局部文件头的开始。对于Zip
文件格式,这个签名固定为十六进制值0x04034b50
,因此当该字段不符合固定值时,自然被认定为文件异常或损坏,从而抛出异常。 - 为什么对于 Excel 的解析,要使用Zip格式进行处理呢?
Excel
文件本身不是Zip
压缩格式,但其包含了压缩技术,从Excel 2007
开始,Microsoft
采用了基于XML的开放文件格式(如xlsx
、xlsm
),这些文件本质上是由多个XML
文件组成的压缩包,通过特定结构存储数据
最佳实践
在应用开发过程中,对于加密文档,对此类异常进行单独 catch
,提示用户文件已加密,而非简单抛出异常,从而优化用户体验
Code
using ICSharpCode.SharpZipLib.Zip;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
namespace MonitCode
{
internal class Program
{
static void Main(string[] args)
{
string filePath = @"D:\test.xlsx";
try
{
using Stream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
if (null == fileStream)
{
throw new NullReferenceException("Extract fileStream failed");
}
IWorkbook workBook = new XSSFWorkbook(fileStream);
//do something
}
catch (ZipException)
{
Console.WriteLine("Excel文件已加密, 请先解密");
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
}
结语
说到这里,既然 Excel
包含 Zip
压缩技术,那么,能否使用处理压缩包的方式,对于 Excel
进行一些有意思的操作呢,欢迎大家评论区留言讨论
相关链接
Zip
文件格式规范文档:https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-6.2.0.txt