谈谈 为什么SharpZipLib不支持文件属性还原(在网上找了半天,也只有提问没有解答)

相信大家做过c#压缩工具的,都知道大名鼎鼎的 SharpZipLib ,因为免费.呵呵.

前一阵子,小弟拿来研究了一下,感觉其实现方式 和微软msdn上的范例程序其实差不多.

不过比msdn好的是动态支持了 大容量文件压缩

     通过UseZip64 这个 属性来控制:      /// <summary> /// Determines how entries are tested to see if they should use Zip64 extensions or not. /// </summary> public enum UseZip64 { /// <summary> /// Zip64 will not be forced on entries during processing. /// </summary> Off, /// <summary> /// Zip64 should always be used. /// </summary> On, /// <summary> /// #ZipLib will determine use based on entry values when added to archive. /// </summary> Dynamic, }


但是在使用过程中,发现 所有解压出来的文件属性都丢失了.

而且偶尔发现压缩后zip文件中其实是含有文件属性的,也就是说用winzip 或者winrar解压后,文件属性没有丢失.

表明压缩没有问题,问题出在解压上.

这就好办了,咱们有压缩和解压的源代码,比较一下咯,开源就是好.


比较后,发现,在ZipOutputStream类中 也就是压缩用的类.

有保存文件属性的代码: WriteLeInt(entry.ExternalFileAttributes)

 WriteLeShort(name.Length); WriteLeShort(extra.Length); WriteLeShort(entryComment.Length); WriteLeShort(0); // disk number WriteLeShort(0); // internal file attributes // external file attributes if (entry.ExternalFileAttributes != -1) { WriteLeInt(entry.ExternalFileAttributes); } else { if (entry.IsDirectory) { // mark entry as directory (from nikolam.AT.perfectinfo.com) WriteLeInt(16); } else { WriteLeInt(0); } }

但是其不在创建成员的方法 PutNextEntry() 中,却是在整个压缩动作结束的 Finish()方法中.

经实验后发现: 所有的文件属性都是存放在 zip文件的末尾,很奇怪为何要这么处理,难道zip标准如此?

所以 当ZipInputStream类中一个一个循环读取成员文件的时候,根本读不到文件属性就不奇怪了.

了解原因后,就好办了. 

自己写了一个类来代替ZipInputStream,当然你也可以改写或者继承ZipInputStream,不过我不推荐,谁知道以后升级版本那个类会变成什么样.

在以流方式 循环读取成员文件后,会遇到一个标志位,ZipConstants.CentralHeaderSignature ,

也就是 ZipOutputStream类中Finish()方法作的第一件事,也就是说,在这以后的数据就是 关于文件的一些附属信息.

 以下是我自己写的方法.来得到文件属性,并且放置在成员对象中.

#region My ZipInputStream Realization /// <summary> /// Advances to the next entry in the archive /// </summary> /// <returns> /// The next <see cref="ZipEntry">entry</see> in the archive or null if there are no more entries. /// </returns> /// <remarks> /// If the previous entry is still open <see cref="CloseEntry">CloseEntry</see> is called. /// </remarks> /// <exception cref="InvalidOperationException"> /// Input stream is closed /// </exception> /// <exception cref="ZipException"> /// Password is not set, password is invalid, compression method is invalid, /// version required to extract is not supported /// </exception> private MyZipEntry GetNextMyEntry() { if (crc == null) { throw new InvalidOperationException("Closed."); } if (entry != null) { CloseEntry(); } int header = inputBuffer.ReadLeInt(); if (header == ZipConstants.EndOfCentralDirectorySignature || header == ZipConstants.CentralHeaderDigitalSignature || header == ZipConstants.ArchiveExtraDataSignature || header == ZipConstants.Zip64CentralFileHeaderSignature) { // No more individual entries exist //Close(); return null; } if (header == ZipConstants.CentralHeaderSignature) { int VersionMadeBy = inputBuffer.ReadLeShort(); int entryVersion = inputBuffer.ReadLeShort(); int entryFlags = inputBuffer.ReadLeShort(); short Method = (short)inputBuffer.ReadLeShort(); int dos_time = inputBuffer.ReadLeInt(); int _crc = inputBuffer.ReadLeInt(); int CompressedSize = inputBuffer.ReadLeInt(); int entrySize = inputBuffer.ReadLeInt(); int nameLength = inputBuffer.ReadLeShort(); int extraLength = inputBuffer.ReadLeShort(); int entryCommentLength = inputBuffer.ReadLeShort(); int _0 = inputBuffer.ReadLeShort(); int _1 = inputBuffer.ReadLeShort(); int ExternalFileAttributes = inputBuffer.ReadLeInt(); int entryOffset = inputBuffer.ReadLeInt(); byte[] namebuffer = new byte[nameLength]; inputBuffer.ReadRawBuffer(namebuffer); string _name = ZipConstants.ConvertToStringExt(entryFlags, namebuffer); namebuffer = new byte[extraLength]; inputBuffer.ReadRawBuffer(namebuffer); byte[] extra = namebuffer; namebuffer = new byte[entryCommentLength]; inputBuffer.ReadRawBuffer(namebuffer); string Comment = ZipConstants.ConvertToStringExt(entryFlags, namebuffer); MyZipEntry headEntry = new MyZipEntry(_name); headEntry.IsMyZipEntry = true; headEntry.ExternalFileAttributes = ExternalFileAttributes; headEntry.Comment = Comment; return headEntry; } // -jr- 07-Dec-2003 Ignore spanning temporary signatures if found // Spanning signature is same as descriptor signature and is untested as yet. if ((header == ZipConstants.SpanningTempSignature) || (header == ZipConstants.SpanningSignature)) { header = inputBuffer.ReadLeInt(); } if (header != ZipConstants.LocalHeaderSignature) { throw new ZipException("Wrong Local header signature: 0x" + String.Format("{0:X}", header)); } short versionRequiredToExtract = (short)inputBuffer.ReadLeShort(); flags = inputBuffer.ReadLeShort(); method = inputBuffer.ReadLeShort(); uint dostime = (uint)inputBuffer.ReadLeInt(); int crc2 = inputBuffer.ReadLeInt(); csize = inputBuffer.ReadLeInt(); size = inputBuffer.ReadLeInt(); int nameLen = inputBuffer.ReadLeShort(); int extraLen = inputBuffer.ReadLeShort(); bool isCrypted = (flags & 1) == 1; byte[] buffer = new byte[nameLen]; inputBuffer.ReadRawBuffer(buffer); string name = ZipConstants.ConvertToStringExt(flags, buffer); entry = new MyZipEntry(name, versionRequiredToExtract); entry.Flags = flags; entry.CompressionMethod = (CompressionMethod)method; if ((flags & 8) == 0) { entry.Crc = crc2 & 0xFFFFFFFFL; entry.Size = size & 0xFFFFFFFFL; entry.CompressedSize = csize & 0xFFFFFFFFL; entry.CryptoCheckValue = (byte)((crc2 >> 24) & 0xff); } else { // This allows for GNU, WinZip and possibly other archives, the PKZIP spec // says these values are zero under these circumstances. if (crc2 != 0) { entry.Crc = crc2 & 0xFFFFFFFFL; } if (size != 0) { entry.Size = size & 0xFFFFFFFFL; } if (csize != 0) { entry.CompressedSize = csize & 0xFFFFFFFFL; } entry.CryptoCheckValue = (byte)((dostime >> 8) & 0xff); } entry.DosTime = dostime; // If local header requires Zip64 is true then the extended header should contain // both values. // Handle extra data if present. This can set/alter some fields of the entry. if (extraLen > 0) { byte[] extra = new byte[extraLen]; inputBuffer.ReadRawBuffer(extra); entry.ExtraData = extra; } entry.ProcessExtraData(true); if (entry.CompressedSize >= 0) { csize = entry.CompressedSize; } if (entry.Size >= 0) { size = entry.Size; } if (method == (int)CompressionMethod.Stored && (!isCrypted && csize != size || (isCrypted && csize - ZipConstants.CryptoHeaderSize != size))) { throw new ZipException("Stored, but compressed != uncompressed"); } // Determine how to handle reading of data if this is attempted. if (entry.IsCompressionMethodSupported()) { internalReader = new ReadDataHandler(InitialRead); } else { internalReader = new ReadDataHandler(ReadingNotSupported); } return entry; } /// <summary> /// return all zipentry int the archive /// </summary> /// <returns> /// all entries <see cref="ZipEntry">entrie</see> /// </returns> /// <remarks> /// If the previous entry is still open <see cref="CloseEntry">CloseEntry</see> is called. /// </remarks> /// <exception cref="InvalidOperationException"> /// Input stream is closed /// </exception> /// <exception cref="ZipException"> /// Password is not set, password is invalid, compression method is invalid, /// version required to extract is not supported /// </exception> public ArrayList GetAllEntrys() { MyZipEntry myEntry; ArrayList rtnEntrys = new ArrayList(); Hashtable mapEntrys = new Hashtable(); int i = 0; while ((myEntry = GetNextMyEntry()) != null) { if (myEntry.IsMyZipEntry) { MyZipEntry rEntry = (MyZipEntry)rtnEntrys[(int)mapEntrys[myEntry.Name]]; rEntry.ExternalFileAttributes = myEntry.ExternalFileAttributes; continue; } rtnEntrys.Add(entry); mapEntrys.Add(entry.Name, i); i++; } return rtnEntrys; } #endregion

zs3897421要求,放上代码文件

1:MyZipEntry.cs  点击打开链接

2:MyZipInputStream.cs  点击打开链接

3:解压示例  点击打开链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值