2015-12-30 17 views
0

我在使用我的PHP下載腳本來處理不帶英文字母的特殊字母字符(如「æøå」)時遇到問題。包含這些字母的文件無法找到,我想知道是否有某種編碼問題。這些文件存儲在運行XAMPP的Windows機器上。使用我的php下載腳本無法下載帶有特殊字符的文件

$getFile = $_SESSION['base'].$_GET['file']; 
$getFile = mb_convert_encoding($getFile, "UTF-8"); 

if (file_exists($getFile)) { //Retrives the file in path $getFile 
    header('Content-Description: File Transfer'); 
    header('Content-Type: application/octet-stream'); 
    header('Content-Disposition: attachment; filename="'.basename($getFile).'"'); 
    header('Expires: 0'); 
    header('Cache-Control: must-revalidate'); 
    header('Pragma: public'); 
    header('Content-Length: ' . filesize($getFile)); 
    readfile($getFile); 
    exit; 
} 

分配到$的GetFile該字符串可以這個樣子「文件/項目/ Abrahallen/administrasjon/ExempelPåadmin.txt」

所以,當有特殊字符的文件名請求file_exists沒有找到一個文件,如果我註釋掉if語句,我得到這個錯誤信息

陣列 ( [文件] =>/ExempelPåadmin.txt ) 文件/項目/ Abrahallen/administrasjon/ExempelPåadmin.txt
警告:文件大小():統計在ç失敗的文件/項目/ Abrahallen/administrasjon/ExempelPåadmin.txt:\ XAMPP \ htdocs中\ files.php上線

警告 :ReadFile的(文件/項目/ Abrahallen/administrasjon/ExempelPåadmin.txt):未能打開流:在C無這樣的文件或目錄:\ XAMPP \ htdocs中\ files.php上線

+0

究竟是什麼問題?請更具體一些,如果有的話添加錯誤消息。 – Corubba

+0

那麼腳本無法找到像「æøå」這樣的字母的文件,但會發現其他文件不包含這些字符 – Johngear

回答

0

當您嘗試使用realpath正在生成文件路徑。

像:

$getFile = $_SESSION['base'].$_GET['file']; 
$getFile = realpath($getFile); 
// This may or may not be needed... 
$getFile = mb_convert_encoding($getFile, "UTF-8"); 
+0

這沒有用。我知道該文件的路徑是正確的,因爲它可以下載該目錄中不包含特殊字符的所有其他文件。 – Johngear

+0

[這個問題]的答案(http://stackoverflow.com/questions/1580475/file-name-with-special-characters-like-%C3%A9-not-found)可能會幫助你。如果您通過服務器上的腳本存儲這些文件,那麼您應該考慮鏈接回答中建議的文件名編碼。 – segFault

0
$getFile = $_SESSION['base'].$_GET['file']; 

首先,這是危險的。文件名可以包括像..這樣的序列,它們將會轉義該目錄,允許在服務器上訪問任何文件,而不僅僅是在base目錄中的文件。這個文件路徑需要強大的驗證。

$getFile = mb_convert_encoding($getFile, "UTF-8"); 

這可能不是正確的。您正在從internal_encoding將字符串轉換爲UTF-8。這可能是UTF-8(在這種情況下,這個什麼也不做),也可能是環境定義(在這種情況下,它是不可靠的,當你部署到不同的服務器將打破)。無論哪種方式,你最終會得到一個不同的字符串到你放入的字符串,這將不匹配文件系統上的文件,因此找不到文件。

因此擺脫這一行的,你會被治療file參數作爲一個普通的一系列字節。如果您(使用scandir()列出文件並創建通過附加'?file='.urlencode($filename)鏈接到他們EG)生成鏈接到你自己的腳本,那麼這將被罰款。

那麼,大多數罰款。如果腳本部署在Linux或OS X服務器上,則可以通過這種方式訪問​​所有文件名。但是在Windows服務器上,該文件系統本身使用Unicode,當你訪問使用一個字節的字符串(如PHP,並使用標準的C stdio接口做其他應用程序)的,Windows中使用「ANSI」代碼頁,這些字節轉換爲Unicode,這總是一些糟糕的傳統語言環境特定的編碼,從來沒有UTF-8。

因此,在西方(ANSI代碼頁1252)Windows安裝中,您將能夠訪問Exempel på admin.txt,但由於其中包含非西方字符,您將無法訪問Příklady admin.txt。而且,將服務移動到其他服務器時,URL的含義可能會發生變化。例如,如果您從Windows服務器轉到Linux服務器,或者將西方Windows服務器轉換爲中文服務器,那麼file參數的隱式編碼將會更改,並且具有非ASCII字符的舊鏈接將中斷。

一般來說,更好的處理方法是將參數視爲始終爲UTF-8,並使用Windows自己的Unicode本機函數而不是C標準庫訪問文件系統。不幸的是PHP沒有能力調用這些內置的函數,所以這很難做到。

一般來說,從PHP腳本訪問本地文件名是很難安全的,如果有任何方法可以避免它,你應該。例如,如果您自己編寫文件名(而不是提供現有的文件目錄),那麼您可以應用自己的臨時編碼(例如十六進制編碼的UTF-8)以避免棘手的字符。或者使用存儲在數據庫中的文件ID。

header('Content-Disposition: attachment; filename="'.basename($getFile).'"'); 

得到這個參數權也是痛苦的負擔。詳情請參閱this question

+0

感謝您的詳細解答。如果用戶沒有通過Web界面訪問它的適當權限,您可以推薦任何其他方式讓用戶訪問不同級別的訪問權限,以訪問受其他人限制的目錄嗎?因爲它似乎沒有安全和方便地做到這一點的方法。 – Johngear

+0

是的,那時你會希望存儲用戶列表和他們在數據庫中訪問的存儲區,提供一個登錄界面,並在允許'readfile'繼續之前檢查數據庫中的授權。 – bobince