当前位置: 代码迷 >> C# >> C# ZIP解压 完全毋庸第三方lib
  详细解决方案

C# ZIP解压 完全毋庸第三方lib

热度:116   发布时间:2016-05-05 03:47:05.0
C# ZIP解压 完全无需第三方lib
使用过程中,能够正常解压一般zip(非Gzip)文件,但是文件内不能有中文文件名(和文件夹名),
只要zip压缩包内出现了中文文件名,那么就会出错,提示路径中含有非法字符。

请问怎样才能让这段程序支持zip内含有中文的文件名?

代码如下:
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Packaging;
using System.Reflection;
using System.Windows.Forms;

namespace ZipHelper
{
    class ZipArchive : IDisposable
    {
        private object external;
        private ZipArchive() { }
        public static ZipArchive OpenOnFile(string path)
        {
            FileMode mode = FileMode.Open;
            FileAccess access = FileAccess.Read;
            FileShare share = FileShare.Read;
            bool streaming = false;
            var type = typeof(Package).Assembly.GetType("MS.Internal.IO.Zip.ZipArchive");
            var meth = type.GetMethod("OpenOnFile", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
            return new ZipArchive { external = meth.Invoke(null, new object[] { path, mode, access, share, streaming }) };
        }
        public void Dispose()
        {
            ((IDisposable)external).Dispose();
        }
        public IEnumerable<ZipFileInfo> Files
        {
            get
            {
                var meth = external.GetType().GetMethod("GetFiles", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                var coll = meth.Invoke(external, null) as System.Collections.IEnumerable;
                foreach (var p in coll) yield return new ZipFileInfo { external = p };
            }
        }
        public struct ZipFileInfo
        {
            internal object external;
            private object GetProperty(string name)
            {
                return external.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).GetValue(external, null);
            }
            public string Name
            {
                get { return (string)GetProperty("Name"); }
            }
            public Stream GetStream()
            {
                FileMode mode = FileMode.Open;
                FileAccess access = FileAccess.Read;
                var meth = external.GetType().GetMethod("GetStream", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                return (Stream)meth.Invoke(external, new object[] { mode, access });
            }
        }

        /// <summary>
        /// Zip解压并更新目标文件
        /// </summary>
        /// <param name="ZipFile">Zip压缩包路径</param>
        /// <param name="UnZipDir">解压目标路径</param>
        /// <returns></returns>
        public static bool UnZip2(String ZipFile, String UnZipDir)
        {
            try
            {
                UnZipDir = UnZipDir.EndsWith(@"\") ? UnZipDir : UnZipDir + @"\";
                using (var zipfile = ZipArchive.OpenOnFile(ZipFile))
                {
                    foreach (var file in zipfile.Files)
                    {
                        if (!file.Name.EndsWith("/"))
                        {
                            string FilePath = UnZipDir + file.Name.Replace("/", @"\");  //设置解压路径
                            string GreatFolder = FilePath.Substring(0, FilePath.LastIndexOf(@"\"));
                            if (!Directory.Exists(GreatFolder)) Directory.CreateDirectory(GreatFolder);
                            byte[] content = new byte[file.GetStream().Length];
                            file.GetStream().Read(content, 0, content.Length);
                            if (File.Exists(FilePath))                      //跳过相同的文件,否则覆盖更新
                            {
                                if (content.Length == new FileInfo(FilePath).Length) continue;
                            }
                            else
                            {
                                File.WriteAllBytes(FilePath, content);
                            }
                        }
                    }
                }
                return true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                return false;
            }
        }
    }
}

------解决思路----------------------
这个Encoding是无法设置的,因为.NET Framework4.0的代码里已经硬编码为ASCIIEncoding了,你可以去这里看微软的Rerfence Source: http://referencesource.microsoft.com/WindowsBase/Base/MS/Internal/IO/Zip/ZipIOBlockManager.cs.html
private ASCIIEncoding _encoding = new ASCIIEncoding();

建议使用ICSharpCode.SharpZipLib或者升级到.NET Framework 4.5
------解决思路----------------------
幸运的是ASCIIEncoding不是密封(sealed)类,这样就可以继承一个自己的FakeAsciiEncoding类,并设置内部的ASCIIEncoding,具体见如下代码:
        public static ZipArchive OpenOnFile(string path)
        {
            FileMode mode = FileMode.Open;
            FileAccess access = FileAccess.Read;
            FileShare share = FileShare.Read;
            bool streaming = false;
            var type = typeof(Package).Assembly.GetType("MS.Internal.IO.Zip.ZipArchive");
            var meth = type.GetMethod("OpenOnFile", BindingFlags.Static 
------解决思路----------------------
 BindingFlags.Public 
------解决思路----------------------
 BindingFlags.NonPublic);

            var external = meth.Invoke(null, new object[] { path, mode, access, share, streaming });
            FieldInfo blockManagerField = type.GetField("_blockManager", BindingFlags.Instance 
------解决思路----------------------
 BindingFlags.NonPublic);
            object blockManager = blockManagerField.GetValue(external);
            FieldInfo encodingField = blockManager.GetType().GetField("_encoding", BindingFlags.Instance 
------解决思路----------------------
 BindingFlags.NonPublic);
            encodingField.SetValue(blockManager, new FakeAsciiEncoding(System.Text.Encoding.GetEncoding(936)));
            return new ZipArchive { external = external };
        }


    public sealed class FakeAsciiEncoding : System.Text.ASCIIEncoding
    {
        private readonly System.Text.Encoding encoding;

        public FakeAsciiEncoding(System.Text.Encoding encoding)
        {
            this.encoding = encoding;
        }

        public override byte[] GetBytes(string s)
        {
            return this.encoding.GetBytes(s);
        }

        public override string GetString(byte[] bytes)
        {
            return this.encoding.GetString(bytes);
        }
    }


FakeAsciiEncoding也可能需要Override其他的方法。你试一下使用哪一种编码适合你的需求。修改System.Text.Encoding.GetEncoding(936)即可。
  相关解决方案