URL 含中文路徑名稱的終極解法

URL 含中文路徑名稱的終極解法 – 利用 mod_fileiri 解決中文檔名問題

當然,對付中文檔名問題的最好的解決方法就是「絕對不要用中文檔名」,然而在許多不得已的狀況下,我們還是被迫要使用中文檔名,這時候問題就來了….

在 Web Server 上使用中文檔名最常遇到的問題就是會發生無法存取的錯誤

發生這種狀況最主要的原因就是在 URL 的定義當中,並沒有任何關於字元集(Charset)的資訊。只有要求對於 URL 中的非 ASCII 符號,要用百分比符號的方式去編碼 (例如: %A4 )

舉個例子來說,對於下面這個含中文檔名的 URL

http://bbs.giga.net.tw/fileiri/中文.html

由於「中文」兩個字並非合法的 ASCII 字元,因此當你在瀏覽器的網址列輸入上面這串 URL 時,瀏覽器會把非 ASCII 字元部分轉換成 %HH 的形式輸出
(關於 URL 的編碼方式,可參閱 Non-ASCII characters in URI attribute values 一文的說明)

但是,URL 中的中文字到底要用 BIG5 還是用 UTF-8 字元集來表示並編碼呢?

在微軟的 IE 裡面,工具→網際網路選項→進階 有個選項「永遠將 URL 傳送成 UTF-8
如果是英文版則是「Always send URLs as UTF-8

這個選項打勾的時候(這也是大部分電腦內定的狀況),URL 中的中文字會被當成 Unicode,並用 UTF-8 編碼方式送出,因此 URL 會被瀏覽器偷偷轉成:

http://bbs.giga.net.tw/fileiri/%E4%B8%AD%E6%96%87.html

也就是說,「中文」這兩個字會被瀏覽器用 UTF-8 編碼成「%E4%B8%AD%E6%96%87」的型式後,再把這個編碼過的 URL 送去給伺服器

大部分的中文字用 UTF-8 編碼會變成 3 個 bytes,所以「中文」兩字就變成上面這 6 個 bytes 的編碼「%E4%B8%AD%E6%96%87

但如果前述的「永遠將 URL 傳送成 UTF-8」選項是沒有打勾的,那情況就不一樣了,
這時候 URL 中的中文字會以 BIG5 的形式編碼送出,URL 就會變成這樣:

http://bbs.giga.net.tw/fileiri/%A4%A4%A4%A5.html

每個中文字用 BIG5 編碼會變成 2 個 bytes,所以「中文」這兩個字就變成上面這 4 個 bytes 的編碼「%A4%A4%A4%A5

這是在用戶端瀏覽器的亂象,不同的瀏覽器或甚至只是不同的設定,對同樣中文檔名所送出的 URL 編碼格式都可能會不一樣

那伺服器收到這串 URL 要怎麼處理呢?

如同前面所述,URL 網址本身並不含字元集(Charset)的資訊,因此伺服器當然也無法知道用戶端瀏覽器採用那個字元集來對 URL 中的中文檔名解釋、編碼

所以伺服器的標準動作就變成「收到什麼檔名就去讀什麼檔案」

收到 UTF-8 編碼的 URL,就用這個 UTF-8 的檔案名稱去 File System 找檔案,收到 BIG5 編碼的 URL,就用這個 BIG5 的檔案名稱去 File System 找檔案

所以如果檔案系統中的檔名是採用 UTF-8 編碼,那用 BIG5 編碼的 URL 去找檔案就會找不到… 相反的,如果檔案系統中的檔名是採用 BIG5 編碼,那麼用 UTF-8 編碼的 URL 也會找不到檔案!

尤其在 Apache/UNIX 的環境下,從 Windows 用 FTP 把檔案上傳時,中文檔案名稱大多會使用 BIG5 的檔名格式上傳儲存

所以大多數這類的系統都會要求用戶不要在上述「永遠將 URL 傳送成 UTF-8」這個選項打勾,這樣存取 BIG5 的中文檔名就不會有問題了

不過,這種方式不是很友善,對於大多數的網友而言,要去改瀏覽器設定是很不方便的!

那有沒有辦法讓瀏覽器不用更改設定就可以完美解決呢?

其實是有的,那就是這篇文章要介紹的 mod_fileiri 這個 Apache module

mod_fileiri 的最主要功用就是讓伺服器可以想辦法去判斷 URL 的編碼,然後幫忙做轉碼、轉址的動作,讓伺服器可以同時處理 UTF-8 及其他字元集的 URL 編碼

以下是在 FreeBSD 下安裝及設定 mod_fileiri 的方法:

首先是從 CVS 中取得這個 module:

# fetch http://dev.w3.org/cvsweb/~checkout~/apache-modules/mod_fileiri/mod_fileiri.c

然後用 apxs 來編譯及安裝這個 module… (請用 root 身份執行)

# /usr/local/sbin/apxs -i -a -c mod_fileiri.c
/usr/local/share/apache2/build/libtool .....
/usr/local/share/apache2/build/libtool .....
/usr/local/share/apache2/build/libtool .....
cp .libs/mod_fileiri.so /usr/local/libexec/apache2/mod_fileiri.so
----------------------------------------------------------------------
Libraries have been installed in:
   /usr/local/libexec/apache2

If you ever happen to want to link against installed libraries
in a given directory, LIBDIR, you must either use libtool, and
specify the full pathname of the library, or use the `-LLIBDIR'
flag during linking and do at least one of the following:
   - add LIBDIR to the `LD_LIBRARY_PATH' environment variable
     during execution
   - add LIBDIR to the `LD_RUN_PATH' environment variable
     during linking
   - use the `-Wl,--rpath -Wl,LIBDIR' linker flag

See any operating system documentation about shared libraries for
more information, such as the ld(1) and ld.so(8) manual pages.
----------------------------------------------------------------------
grep: /usr/local/libexec/apache2/mod_fileiri.la: No such file or directory
grep: /usr/local/libexec/apache2/mod_fileiri.la: No such file or directory
Warning!  dlname not found in /usr/local/libexec/apache2/mod_fileiri.la.
Assuming installing a .so rather than a libtool archive.
chmod 755 /usr/local/libexec/apache2/mod_fileiri.so
[activating module `fileiri' in /usr/local/etc/apache2/httpd.conf]

apxs 不但會產生 mod_fileiri.so 並把它複製到 /usr/local/libexec/apache2/ 下面,
甚至還會幫你把 httpd.conf 的設定改好喔!

然後你只要重新啟動 Apache 就完成了,簡單吧!

# /usr/local/etc/rc.d/apache2.sh restart

接下來就要開始設定 mod_fileiri 的工作了…

mod_fileiri 有三個 directives 可用,分別是 FileIRIFilenameCharsetOldFilenameCharset,位置可以放在 Server Config / Directory / Virtual Host 裡面,甚至 .htaccess 裡面也可..

FileIRI 有四個選項: Off、On、Backwards、Only

Off 就不用說了,設定成 Off 等於是沒有設定的狀況

On 則是指檔案系統上面的目錄或檔案名稱使用的是比較舊的編碼方式(Legacy Encoding),例如 BIG5,然後提供檔案給所有採用 UTF-8 編碼過的 URL,同時,如果 mod_fileiri 發現 URL 並不是採用 UTF-8 編碼,就會對該 URL 做一個 HTTP/1.0 301 Moved Permanently,把它 redirect 到 UTF-8 型式的 URL

如果以前述的例子來看,設定應該是這樣的 (我習慣直接設在目錄的 .htaccess 下面)

<IfModule mod_fileiri.c>
  FileIRI          On
  FilenameCharset  Big5
</IfModule>

這樣設定之後,我們用 wget 來測試一下:

# wget -S -O /dev/null http://bbs.giga.net.tw/fileiri/中文.html
--10:29:34--  http://bbs.giga.net.tw/fileiri/%A4%A4%A4%E5.html
           => `/dev/null'
Resolving bbs.giga.net.tw... 203.187.29.180
Connecting to bbs.giga.net.tw|203.187.29.180|:80... connected.
HTTP request sent, awaiting response...
  HTTP/1.0 301 Moved Permanently
  Date: Thu, 08 Sep 2005 02:29:37 GMT
  Server: Apache/2.0.54 (FreeBSD) PHP/5.0.4
  Location: http://bbs.giga.net.tw/fileiri/%e4%b8%ad%e6%96%87.html
  Content-Length: 354
  Content-Type: text/html; charset=iso-8859-1
  X-Cache: MISS from WebAmpRP@GIGAMEDIA
  Connection: keep-alive

Location: http://bbs.giga.net.tw/fileiri/%e4%b8%ad%e6%96%87.html [following]
--10:29:34--  http://bbs.giga.net.tw/fileiri/%e4%b8%ad%e6%96%87.html
           => `/dev/null'
Reusing existing connection to bbs.giga.net.tw:80.
HTTP request sent, awaiting response...
  HTTP/1.0 200 OK
  Date: Wed, 07 Sep 2005 12:36:30 GMT
  Server: Apache/2.0.54 (FreeBSD) PHP/5.0.4
  Last-Modified: Wed, 07 Sep 2005 12:31:33 GMT
  ETag: "30bd3-14-b986f340;bc359880"
  Accept-Ranges: bytes
  Content-Length: 20
  Content-Type: text/html
  Age: 451
  X-Cache: HIT from WebAmpRP@GIGAMEDIA
  Connection: keep-alive
Length: 20 [text/html]

100%[====================================>] 20

10:29:34 (1.59 MB/s) - `/dev/null' saved [20/20]

從上面可以看到,原本用 BIG5 編碼的 URL,被 redirect 成 UTF-8 型式的 URL,然後就可以正確取得檔案,而這個檔案「中文.html」在檔案系統上是用 BIG5 型式命名的

因此,利用 FileIRI On 就可以順利解決大部分的中文檔名問題了!

那如果你的檔案名稱是採用 UTF-8 命名的呢?這時候你就需要使用 Backwards 這個選項,例如:

<IfModule mod_fileiri.c>
  FileIRI             Backwards
  OldFilenameCharset  Big5
</IfModule>

上述設定的意思是說,所有檔案系統上的檔案都是用 UTF-8 命名,但是對於不是使用 UTF-8 編碼方式的 URL,就會把它 redirect 到 UTF-8 的版本,一樣可以提供服務

FileIRI 還有另外一個選項 Only,這個選項則是設定只提供服務給 UTF-8 編碼過的 URL,而檔案系統上的檔案則是使用 Legacy Encoding。這個方式較不常用,就不多作介紹了

各種設定組合產生的效果可以參考 mod_fileiri 原始網站上面的說明:

http://www.w3.org/2003/06/mod_fileiri/

關於 URL 的字元集編碼問題,可以參考下面這篇更詳細的說明:

An Introduction to Multilingual Web Addresses – Handling the path

在〈URL 含中文路徑名稱的終極解法〉中有 13 則留言

  1. alex

    請問一下 除了用ports安裝的apache2 可以使用這個模組外,
    我用tar 編的apache2 一直無法成功的編出 mod_fileiri.so ,不曉得有其他安裝mod_fileiri的方法嗎?

    回覆
  2. dada 文章作者

    看你的 apache 裝在哪裡,假設裝在 /usr/local/apache/ 下面,那請改用:

    # /usr/local/apache/bin/apxs -i -a -c mod_fileiri.c

    回覆
  3. alex

    hi 是的, 因為最後mod_fileiri.so 一直無法產生,想說是那邊漏掉了
    謝謝您的回覆,如下最後兩行出現的錯誤

    # /usr/local/apache/bin/apxs -i -a -c mod_fileiri.c

    Libraries have been installed in:
    /usr/local/apache/modules

    If you ever happen to want to link against installed libraries
    in a given directory, LIBDIR, you must either use libtool, and
    specify the full pathname of the library, or use the `-LLIBDIR’
    flag during linking and do at least one of the following:
    – add LIBDIR to the `LD_LIBRARY_PATH’ environment variable
    during execution
    – add LIBDIR to the `LD_RUN_PATH’ environment variable
    during linking
    – use the `-Wl,–rpath -Wl,LIBDIR’ linker flag

    See any operating system documentation about shared libraries for
    more information, such as the ld(1) and ld.so(8) manual pages.
    ———————————————————————-
    Warning! dlname not found in /usr/local/apache/modules/mod_fileiri.la.
    Assuming installing a .so rather than a libtool archive.
    chmod 755 /usr/local/apache/modules/mod_fileiri.so
    chmod: /usr/local/apache/modules/mod_fileiri.so: No such file or directory
    apxs:Error: Command failed with rc=65536

    回覆
  4. dada 文章作者

    我想有可能是您的 libtool 的版本問題,
    我這邊有兩台機器使用 libtool 1.5.6 及 libtool 1.5.22 都可以正確安裝成功,
    您要不要檢查一下您使用的 libtool 版本:

    # libtool --version

    回覆
  5. alex

    hi 十分感謝,原來是/usr/local/apache/build/libtool的問題
    把他改為系統的libtool後就可以了..謝謝

    回覆
  6. incense burner

    At this time it appears like Expression Engine is
    the top blogging platform available right now. (from what I’ve read) Is that what you are using on your blog?

    回覆
  7. Crossfit competition

    I love your blog.. very nice colors & theme.
    Did you create this website yourself or did you hire someone to do it for you?
    Plz reply as I’m looking to construct my own blog and would like
    to know where u got this from. thanks

    回覆
  8. 自動引用通知: card debt

  9. 自動引用通知: 北海道自由行

歡迎留下您的意見