2012年3月8日 星期四

避免使用者下載的Zip檔裡有亂碼

最近某個專案是做對岸的案子, 所以該專案主機理所當然的就是用簡體OS的主機囉.
某天發現問題來了, 以前都能下載的報表, 突然發現異常.

1. 直接下載後開啟, 會發現ZIP檔裡都是空的
2. 若是儲存後再用檔案總管開啟, 發現檔案變亂碼且有部份檔案不見

3. 再用7-ZIP去開啟壓縮檔, 會看到所有檔案, 但是所有檔名都變成亂碼


研究了很久, 還換了一個壓ZIP的元件(原本是用ShapZipLib, 後來改用DotNetZip)
因為來源的報表檔案的編碼是UTF-8, 所以我在壓ZIP檔時, 一直都覺得應該ZIP檔也是要壓成UTF-8或是Unicode, 這樣的放到各種OS的主機上, 看到的字才會正確顯示; 結果當然是什麼鬼也沒看到 =.=q

中間的甘苦就不提了, 直接說結論:
a. ZIP檔的編碼必須指定為使用者OS的語系
b. ZIP檔內的檔案名文字也必須轉換為使用者OS的語系

P.S 假設有一個Archive.zip的zip檔, 裡面壓了一個"每日結算報表.TXT"
這個TXT本身的編碼可能是UTF-8或是其它任何編碼,
但是不會影響上面下的結論

以下是ASP.NET使用DotNetZip輸出二種語系(簡體 & 繁體)ZIP檔的程式碼.

//使用前先下載DotNetZip, 並且引用其中的Ionic.Zip.dll
Response.Clear();
string zipFilename = DateTime.Now.ToString("yyyMMddHHmmss") + ".zip";
Response.ContentType = "application/zip";
Response.AddHeader("content-disposition", "filename=" + zipFilename);
Encoding sysLocate = Encoding.GetEncoding(936); //預設ZIP檔的檔案編碼為簡體中文
//判斷Client的語系
if (Request.Headers["Accept-Language"].ToUpper() == "ZH-TW")
{
    sysLocate= Encoding.GetEncoding(950);   //繁體中文
}
using (ZipFile zip = new ZipFile(sysLocate))
{
    //files變數是輸出檔案清單的完整路徑
    foreach (string file in files)
    {
        ZipEntry entry = zip.AddFile(file, "");
        if (Request.Headers["Accept-Language"].ToUpper() == "ZH-TW")
        {
            entry.FileName = ToTraditional(entry.FileName); //把簡體檔名換成繁體檔名
        }
        logger.Info(entry.FileName);
    }
               
    zip.Save(Response.OutputStream);
}
Response.End();


4. 以下圖片是二種不同語系OS去下載報表, 得到的不同結果.
使用DotNetZip, 在壓縮時設定ZIP檔的編碼, 且轉換所有檔案的檔名

A.繁體主機的使用者


B. 簡體主機的使用者


沒有留言:

張貼留言