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. 簡體主機的使用者


2011年3月21日 星期一

OralceClient預設版本問題

原本公司的專案都是採用.net framework 2.0
可是這次接手的是採用.net framework 4.0
原本在本機RUN時都沒有問題, 可是放到IIS上卻出現以下錯誤

System.Data.OracleClient requires Oracle client software version 8.1.7 or greater.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Exception: System.Data.OracleClient requires Oracle client software version 8.1.7 or greater.

一開始以為是web.config設定的版本有問題, 結果一看設定如下:
<add assembly="System.Data.OracleClient, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
因為是找不到特定版本, 所以就去追Assembly, 發現系統裡並沒有.4.X版

接著試著改成
<add assembly="System.Data.OracleClient, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
結果得到同樣的錯誤, 感覺程式還是去抓4.X的版本


追了好久, 中間一度把framework版本降回2.0 後來才發現
原來專案中某支程式定義了預設值如下:
因為這裡定義了預設值, 而且我在web.config裡又沒有設定預設值,
結果就是程式裡定義的為準.

修改方法就是在web.config裡加上這段


在web.config指定特定版本的OracleClient版本即可解決問題