使用过程中,能够正常解压一般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)即可。