您在這裡

上傳中文檔名的問題

hom's 的頭像
hom 在 2008-10-05 (周日) 15:18 發表

做了一下測試
發現是 basename() 這個函式的問題
在 file_check_upload($source = 'upload') 這個 Drupal 處理上傳檔案的函式內
使用了 $file->filename = trim(basename($_FILES["files"]["name"][$source]), '.'); 來取得檔案名稱 (includes/file.inc 224行)

不過 basename() 這個函式有一些問題 (http://www.php.net/manual/en/function.basename.php#79018)

有人提到
Attention:
This function is NOT UTF-8 safe.
If the filename starts with an utf8 character, this character will be cut off.
Example:
basename('Übergabe')='bergabe'

Work around:
Just insert a space at the beginning of the string:
basename(' Übergabe')=' Übergabe'

Use this call as workaround:
ltrim(basename(' '+$s))

例如上傳 test.zip 的檔案
basename() 會傳回 test.zip
但是上傳 測試.zip 的檔案
basename() 會傳回 .zip
然後頭尾的 . 又會被 trim([filename], '.') 給去掉
變成 zip
造成後續在判斷檔案類型的時候會抓不到 .zip

basename() 的這個問題
可以在傳入的字串前面多加一個空白
就可以解決這個問題
改為 basename(' '.$_FILES["files"]["name"][$source]);
就會傳回 測試.zip
然後再用 trim() 把加上去的空白去掉就行了
這樣就會取得正確的檔名

$file->filename = trim(trim(basename(' '.$_FILES["files"]["name"][$source])), '.');

以上是在 windows 底下用 xampp 做的測試

在 linux 底下 basename() 的判斷似乎會有不同
加上檔名編碼的差異
應該要另外做測試才有辦法確認了

依照你所說的,我在 Debian lenny zh_TW.UTF-8 + Drupal-6.5 做了以下 modification 之後,上傳中文檔名沒有問題,謝謝。不過我想與 :%s/basename(\(.\{-}\))/urldecode(basename(drupal_urlencode(\1)))/g 是等同的,就是要避開 basename() 的問題。

由於我的檔案系統使用的是 utf-8 是沒問題。如果是繁體中文版的 Windows 預設的檔案系統應該是 big5,可能會出問題吧,或許利用 iconv 試圖 utf-8 轉 big5 吧,只是 big5 定址字集有限,繁體中文字、少量簡體日文字、少量日文字。還是使用支援 utf-8 的檔案系統來架設吧!你可以想像你繁體中文版的 Windows 同時存放檔名為簡體中文、繁體中文、日文、韓文等多位元文字嗎?

或者還是先用過渡性的做法,存放檔案的檔名使用 ascii 的檔案編號就好,其他就靠資料庫中的編號對應檔名。

$drupal/includes/file.inc

我分別做了兩個解決的 file.inc 檔案:
1. 針對所有 basename() 之前先 urlencode 之後 urldecode。
:%s/basename(\(.\{-}\))/urldecode(basename(drupal_urlencode(\1)))/g

2. 前綴空白字元後 trim:
:%s/\(trim(basename(\)\(.\{-})\)/trim(\1' ' . \2)/

如果你的檔案系統非 utf-8 可能會出現:

* warning: move_uploaded_file(sites/default/files/新增文字文件.txt) [function.move-uploaded-file]: failed to open stream: Invalid argument in E:\home\yjchen\public_html\d6\includes\file.inc on line 573.
* warning: move_uploaded_file() [function.move-uploaded-file]: Unable to move 'C:\WINDOWS\TEMP\phpE8.tmp' to 'sites/default/files/新增文字文件.txt' in E:\home\yjchen\public_html\d6\includes\file.inc on line 573.
* 檔案上傳錯誤。無法移動上傳的檔案。

讚,其實應該commit到drupal.org上面去 :P
明天來看看好了
--
from open mind to open source~

--
from open mind to open source~

PHP 惹的禍的嗎? XDD

正好手邊有幾個系統環境可以測看看,如下..

1. 不用改程式碼,可以在客戶端 ( Windows, Linux ) 正常上傳中文檔名的伺服器:
Apache 2.2.4 / PHP 5.2.2 (FreeBSD 6.1)
Apache 1.3.39 / PHP 4.4.7 (CentOS 4.7)

2. 上傳中文檔名有問題的伺服器:
Apache 2.2.9 / PHP 5.2.6 (Ubuntu 8.10 + XAMPP)
Apache 2.2.10 / PHP 5.2.6 (Arch linux Overlord kernel 2.6.27)
Apache 2.2.9 / PHP 5.2.6 (CentOS 5.2)

再來參照 hom 提的補前綴空白字元解決辦法,在上列第二項上傳中文檔名有問題的伺服器上跑,以 "測試.png" 上傳,存檔的結果為 ".png",失敗。

再接再厲參照 yjchen 提的 urlencode 方法,同樣在上列第二項上傳中文檔名有問題的伺服器上跑,以 "測試.png" 上傳,存檔的結果為 "測試.png",成功。另由 Windows XP 客戶端測也成功。

以上除了 OS 不同外,會失敗的 PHP 版本同樣是 5.2.6,另二個版本 5.2.2, 4.4.7 會成功,但不曉得是否跟Apache, OS 也有關係?! 或是編譯的關係?

至少 Drupal 核心在這個中文檔名存在的問題點上,若沒有共通性的解決方案,上傳中文檔名的檔案時,先改檔名 (加個英數字的前綴符號 ),再上傳也不是那麼方便,況且每個用戶又不儘然皆知。

basename() =.=|||