PHP Conference Japan 2024

move_uploaded_file

(PHP 4 >= 4.0.3, PHP 5, PHP 7, PHP 8)

move_uploaded_file將上傳的檔案移動到新的位置

說明

move_uploaded_file(字串 $from, 字串 $to): 布林值

此函式會檢查以確保 from 指定的檔案是有效的上傳檔案(表示它是透過 PHP 的 HTTP POST 上傳機制上傳的)。如果檔案有效,它將會被移動到 to 所提供的檔案名稱。

如果上傳檔案的操作有任何可能將檔案內容洩漏給使用者,甚至是同系統上的其他使用者,這類型的檢查就格外重要。

參數

from

已上傳檔案的檔名。

to

移動後檔案的目標位置。

傳回值

成功時傳回 true

如果 from 不是有效的上傳檔案,則不會執行任何動作,且 move_uploaded_file() 會傳回 false

如果 from 是有效的上傳檔案,但因某些原因無法移動,則不會執行任何動作,且 move_uploaded_file() 會傳回 false。此外,還會發出警告。

範例

範例 #1 上傳多個檔案

<?php
$uploads_dir
= '/uploads';
foreach (
$_FILES["pictures"]["error"] as $key => $error) {
if (
$error == UPLOAD_ERR_OK) {
$tmp_name = $_FILES["pictures"]["tmp_name"][$key];
// basename() 可以防止檔案系統路徑穿越攻擊;
// 可能需要進一步驗證/過濾檔名
$name = basename($_FILES["pictures"]["name"][$key]);
move_uploaded_file($tmp_name, "$uploads_dir/$name");
}
}
?>

注意事項

注意:

move_uploaded_file() 會受到 open_basedir 的限制。然而,限制僅套用在 to 路徑上,以便允許移動 from 可能與這些限制衝突的上傳檔案。 move_uploaded_file() 藉由僅允許移動透過 PHP 上傳的檔案來確保此操作的安全性。

警告

如果目標檔案已存在,它將被覆蓋。

參見

新增筆記

使用者貢獻的筆記 13 則筆記

Yousef Ismaeil Cliprz
11 年前
使用此函式前必須知道的安全性提示

第一:確保檔案不是空的。

第二:為了更加安全,請確保檔案名稱是由英文字母、數字和 (_-.) 符號組成。

您可以參考以下範例使用函式

<?php

/**
* 檢查 $_FILES[][name]
*
* @param (string) $filename - 上傳的檔案名稱。
* @author Yousef Ismaeil Cliprz
*/
function check_file_uploaded_name ($filename)
{
return (bool) (
preg_match("`^[-0-9A-Z_\.]+$`i",$filename)) ;
}

?>

第三:確保檔案名稱不超過 250 個字元。

範例如下

<?php

/**
* 檢查 $_FILES[][name] 的長度。
*
* @param (string) $filename - 上傳的檔案名稱。
* @author Yousef Ismaeil Cliprz.
*/
function check_file_uploaded_length ($filename)
{
return (bool) ((
mb_strlen($filename,"UTF-8") > 225) );
}

?>

第四:檢查您專案中允許的檔案副檔名和 MIME 類型。您可以使用:pathinfo() https://php.dev.org.tw/pathinfo

或者您可以使用正規表達式來檢查檔案副檔名,範例如下

#^(gif|jpg|jpeg|jpe|png)$#i

或者使用 in_array 檢查,如下所示

<?php

$ext_type
= array('gif','jpg','jpe','jpeg','png');

?>

您有多種選擇來檢查副檔名和 MIME 類型。

第五:檢查檔案大小,並確保 php.ini 中設定的檔案上傳限制符合您的需求,您可以從 https://php.dev.org.tw/manual/en/ini.core.php#ini.file-uploads 開始著手。

最後但同樣重要的是:檢查檔案內容是否包含惡意程式碼或類似 https://php.dev.org.tw/manual/en/function.file-get-contents.php. 函式可以處理的內容。

您可以使用 .htaccess 來阻止上傳路徑中某些指令碼(例如 php 檔案)的執行。

使用

AddHandler cgi-script .php .pl .jsp .asp .sh .cgi
Options -ExecCGI

為了保護您的專案,請勿忘記這些步驟。
matthias dot dailey at gmail dot com
13 年前
目標目錄必須存在;`move_uploaded_file()` 不會自動建立目錄。
adeel dot cs at gmail dot com
9 個月前
權限問題。

如果您在資料夾上設定了 setgid,例如 g+s,並且想知道為什麼建立的檔案屬於 www-data:www-data,請注意上傳的檔案會先以網頁使用者的身分儲存在 /tmp 資料夾中。

`move_uploaded_file()` 指令會將檔案從 /tmp 移動到指定的目標目錄,包含 /tmp 檔案目前的權限。

因此,setgid 會被忽略,並且不會繼承父目錄的權限。
Dan Delaney
16 年前
對於在 Windows 和 IIS 上使用 PHP 的使用者,您應該在 php.ini 中將「upload_tmp_dir」的值設定為網站目錄附近的目錄,建立該目錄,然後將其權限設定為與網站目錄相同的權限。否則,當您上傳檔案且檔案進入 C:\WINDOWS\Temp 後,再將其移動到網站目錄時,其權限將不會正確設定。如果您之後想使用 ImageMagick 的 convert 工具等工具操作該檔案,這會造成問題。
shacker at birdhouse dot org
18 年前
如果您正在處理透過某些外部 FTP 來源上傳的檔案,並且需要將它們移動到最終目的地,在 php.net 中搜尋「mv」或「move」將無法找到您想要的內容。您需要的是 `rename()` 函式。

https://php.dev.org.tw/manual/en/function.rename.php

(`move_uploaded_file()` 將無法運作,因為 POST 變數將不存在。)
Zarel
18 年前
nouncad at mayetlite dot com 發佈了一個上傳檔案的函式,如果檔案已存在,它會將檔案重新命名為 filename[n].ext。

它只適用於副檔名剛好是三個字母的檔案,所以我修正了這個問題(並且同時進行了一些其他改進)。

<?php
// 用法:uploadfile($_FILE['file']['name'],'temp/',$_FILE['file']['tmp_name'])
function uploadfile($origin, $dest, $tmp_name)
{
$origin = strtolower(basename($origin));
$fulldest = $dest.$origin;
$filename = $origin;
for (
$i=1; file_exists($fulldest); $i++)
{
$fileext = (strpos($origin,'.')===false?'':'.'.substr(strrchr($origin, "."), 1));
$filename = substr($origin, 0, strlen($origin)-strlen($fileext)).'['.$i.']'.$fileext;
$fulldest = $dest.$filename;
}

if (
move_uploaded_file($tmp_name, $fulldest))
return
$filename;
return
false;
}
?>
Juliano P. Santos
5 年前
對於那些想使用 inotify-tools 在 move_uploaded_file 將檔案放入特定目錄時觸發事件的人,請注意 move_uploaded_file 會觸發建立事件,而不是 inotify-tools 的移動事件。
Florian S. in H. an der E. [.de]
16 年前
move_uploaded_file(在我的設定中)總是建立權限為 0600 ("rw- --- ---") 的檔案,且檔案擁有者為執行網頁伺服器的使用者(擁有者和群組)。
即使目錄已設定黏著位元 (sticky bit) 到群組權限!
我找不到任何透過 php.ini 甚至使用 "umask()" 來更改此設定的方法。

我希望我伺服器上的普通使用者能夠對目錄執行 "tar cjf" ... 但這會在完全由網頁伺服器處理程序使用者擁有的檔案上失敗;
不過 "copy(from, to)" 函式會遵守黏著位元!
chelidze dot givia at gmail dot com
1 年前
使用 move_uploaded_file() 時,如果使用者上傳已存在名稱的圖片,move_uploaded_file() 會覆蓋它。良好的做法是將圖片儲存在您建立卡片/使用者/產品等時產生的目錄中...

<?php
函數 generateDir(int $n): string {
$characters="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
$dir = "";
for(
$i = 0; $i<$n; $i++){
$index = rand(0, strlen($characters)-1);
$dir .= $characters[$index];
}
return
$dir;
}

$image = $_FILES["image"];
$imagePath = 'images/'. generateDir(10) .'/'. $image["name"];

// 必須先建立目錄,否則上傳將無法繼續。
mkdir($imagePath, 0777, true);

// 一些錯誤處理等等...

move_uploaded_file($image["tmp_name"], $imagePath);
?>
benbrown3882 at gmail dot com
1 年前
請確保上傳暫存目錄和目標目錄具有「其他」的「寫入」權限。
nlgordon at iastate dot edu
17 年前
只是一個有幫助的註釋。如果您設定了 open_basedir,則必須將 upload_tmp_dir 設定為 open_basedir 內的某個位置。否則檔案上傳將會被拒絕。 move_uploaded_file 可能會感知 open_basedir,但上傳過程的其餘部分則不會。
jest3r at mtonic dot net
19 年前
看來 move_uploaded_file 使用的是暫存檔所在位置之父目錄的群組權限,而簡單的「複製」則使用的是 apache 程序的群組。如果您的暫存檔位置的所有者是 root:wheel,這可能會造成安全性上的惡夢。
Rob Szarka
18 年前
顯然上面的警告最好寫成「如果目標檔案已經存在,它將會被覆蓋... 無論目標檔案的權限為何。」

換句話說,move_uploaded_file() 的執行方式如同它是 root,而不是執行網頁伺服器的使用者或執行腳本的所有者。
To Top