1. 程式人生 > >[完]PHP檔案下載 下載GET值中含有中文字元的檔案,儲存中文檔名,檔案不能開啟,遇到的編碼問題

[完]PHP檔案下載 下載GET值中含有中文字元的檔案,儲存中文檔名,檔案不能開啟,遇到的編碼問題

編碼問題

  • 使用Windows Server 2008英文版作為伺服器,檔案系統中檔名儲存的編碼與Windows“當前系統區域設定”相關。(例如:中文預設為GB2312,俄文為西里爾文Windows[windows-1251])

  • 文字編輯器採用的編碼。

    • Windows Notepad預設的文字編碼是ANSI。
    • Notepad++,可以通過“格式 | 轉為UTF-8無BOM編碼格式”,將編碼轉化為UTF-8無BOM格式。採用伺服器的語言版本不一樣時,有所差別,英文Windows 2008伺服器時,PHP檔案格式設為“UTF-8編碼”,這樣下載的檔案才能正確開啟。
  • 開啟下載的檔案出現問題。

    • 使用中文Windows 2008
      伺服器時,PHP檔案編碼格式設為“UTF-8無BOM編碼”
    • 使用英文Windows 2008 伺服器時,PHP檔案格式設為“UTF-8編碼”
  • Web伺服器採用Apache。預設編碼為UTF-8,GET值的編碼也是UTF-8。

# 設定PHP頁採用的編碼,如果使用Windows自帶的Notepad編碼,且不在首行設定編碼,那麼預設採用的是ANSI編碼
header("Content-Type: text/html; charset=UTF-8");
$getFile = $_GET['f'];
echo 'GET filename: ' . $getFile . '<br/>';
# 將get中獲取的filename轉換成os認識的filename,這樣file_exists()才認,才能下載相應檔案。
$osFile = iconv('utf-8','GB2312', $getFile); echo 'OS filename: ' . $osFile . '<br/>'; if( file_exists($osFile) ) { echo 'exist'; } else { echo 'not exist'; }

  http://192.168.1.102/test.php?f=中文.txt的執行結果

GET filename: 中文.txt
OS filename: ����.txt
exist 

  如果去掉上述程式碼的header("Content-Type: text/html; charset=UTF-8");

,採用Notepad,但是另存為編碼“UTF-8”的檔案,執行結果和上面一樣。如果採用Notepad預設ANSI編碼,執行結果則是:

GET filename: 涓枃.txt
OS filename: 中文.txt
exist 
  • basename($pFileName):對含有中文字元的檔名無效,解決方法:
end(explode('/',$pFileName));
  • file_exists($fileName):對解析中文檔名有問題。主要是編碼問題,只有將檔名轉換為Windows伺服器“當前系統區域設定”中語言採用的預設語言編碼才行。
  • IE瀏覽器中,即使頁面預設選擇是UTF-8,連結中中文字串依然使用GB2312編碼,看下面程式碼在不同瀏覽器中執行var_dump()函式的結果
header("Content-Type: text/html; charset=UTF-8");
$getFile = $_GET['f'];

echo 'var_dump getFile: ';
var_dump($getFile);
echo '<br/>';

  PHP頁面程式碼如上,使用http://192.168.1.102/test1.php?f=中文,訪問頁面。在不同瀏覽器中結果。

360,Chrome,Firefox IE
var_dump getFile: string(6) "中文" var_dump getFile: string(4) "����"

使用一種為每個檔案建立一個數字目錄,去目錄下搜尋檔案並下載,繞過GET傳中文檔名。規避了各種編碼問題。但是如果不注意Windows中“當前系統區域設定”的話,由中文伺服器換到英文伺服器上,會出現問題,需要將“當前系統區域設定”設定到“中文(簡體,中國)”。

程式程式碼

下面程式碼可在360,Chrome和Firefox正常使用,但在IE下存在問題。

<?php
header("Content-Type: text/html; charset=UTF-8");
$getFile = $_GET['f'];
# 將瀏覽器傳入的UTF-8編碼的檔名,轉換成Windows伺服器認的GB2312編碼,然後才能下載相關檔案
$osFile = iconv('UTF-8','GB2312', $getFile);
downfile('./d/'.$osFile);

function downfile($osFile) {   
    if(file_exists($osFile)) {
        # basename() 消除檔案路徑,只保留檔名,對於中文出現問題,應該就是編碼問題,
        # 處理好編碼問題,應該可以使用basename()函式
        # $saveName = basename($osFile);
        $saveName = end(explode('/', $osFile)); 
        # 將Windows伺服器認的GB2312編碼,轉換成瀏覽器認的UTF-8編碼
        $saveName = iconv('GB2312', 'UTF-8', $saveName);     
        header("Content-Type: application/octet-stream");
        header("Accept-Ranges: bytes");
        # 計算檔案大小,使用Windows伺服器認的GB2312編碼
        header("Accept-Length: ".filesize($osFile));
        # 在瀏覽器端儲存檔案,使用瀏覽器認的UTF-8編碼
        header("Content-Disposition: attachment; filename=\"{$saveName}\"");
        # 讀取檔案,使用Windows伺服器認的GB2312編碼
        readfile($osFile);
    } else {
        echo "The file is not exist!";
    }
}
?>

下面程式碼可在IE,Chrome和Firefox正常使用,但在360下存在問題。

<?php
header("Content-Type: text/html; charset=UTF-8");
$getFile = $_GET['f'];
# 將瀏覽器傳入的UTF-8編碼的檔名,轉換成Windows伺服器認的GB2312編碼,然後才能下載相關檔案
$osFile = iconv('UTF-8','GB2312', $getFile);
$osPathFile = './d/'.$osFile;

$browser = $_SERVER['HTTP_USER_AGENT'];
# 如果是IE的話
if( strpos($browser, "Trident") > -1 || strpos($browser, "MSIE") > -1) {
    # 獲取儲存檔名
    $saveFile = $getFile;
    downFileIE( $osPathFile, $saveFile);
    # mb_convert_encoding與iconv功能類似,但是它是將第一個引數的原來編碼放在最後一個引數位置,需要轉變的編碼是第二個引數
} else {
    downFile($osPathFile);
}

function downFile($osPathFile) {   
    if(file_exists($osPathFile)) {
        # basename() 消除檔案路徑,只保留檔名,對於中文出現問題,應該就是編碼問題,
        # 處理好編碼問題,應該可以使用basename()函式
        # $saveName = basename($osFile);
        $saveName = end(explode('/', $osPathFile)); 
        # 將Windows伺服器認的GB2312編碼,轉換成瀏覽器認的UTF-8編碼
        $saveName = iconv('GB2312', 'UTF-8', $saveName);    

        header("Content-Type: application/octet-stream");
        header("Accept-Ranges: bytes");
        # 計算檔案大小,使用Windows伺服器認的GB2312編碼
        header("Accept-Length: ".filesize($osPathFile));
        # 在瀏覽器端儲存檔案,使用瀏覽器認的UTF-8編碼
        header("Content-Disposition: attachment; filename=\"{$saveName}\"");
        # 讀取檔案,使用Windows伺服器認的GB2312編碼
        readfile( $osPathFile);
    } else {
        echo "The file is not exist!";
    }
}

function downFileIE($osPathFile, $saveFile) {   
    if(file_exists($osPathFile)) {
        header("Content-Type: application/octet-stream");
        header("Accept-Ranges: bytes");
        # 計算檔案大小,使用Windows伺服器認的GB2312編碼
        header("Accept-Length: ".filesize($osFile));
        # 在瀏覽器端儲存檔案,使用瀏覽器認的UTF-8編碼
        header("Content-Disposition: attachment; filename=\"{$saveFile}\"");
        # 讀取檔案,使用Windows伺服器認的GB2312編碼
        readfile('./d/'.$saveFile);
    } else {
        echo "The file is not exist!";
    }
}
?>

如果伺服器儲存的是其他國家語言的文字,可以統一將程式中GB2312編碼,轉換相應國家的語言編碼,注意在Windows系統設定中,將“當前系統區域設定”設定成相應的國家和語言。