PHP 日本研討會 2024

PUT 方法支援

PHP 提供了對 HTTP PUT 方法的支援,某些用戶端會使用此方法將檔案儲存在伺服器上。PUT 請求比使用 POST 請求上傳檔案簡單得多,它們看起來像這樣

PUT /path/filename.html HTTP/1.1

這通常表示遠端用戶端想要將後續的內容儲存為:網頁樹中的 /path/filename.html。對於 Apache 或 PHP 來說,讓任何人自動覆寫網頁樹中的任何檔案顯然不是一個好主意。因此,為了處理這類請求,您必須先告知您的網頁伺服器,您希望由某個 PHP 指令碼處理該請求。在 Apache 中,您可以使用 Script 指令來完成此操作。它可以放置在 Apache 組態檔中的幾乎任何位置。常見的位置是在 <Directory> 區塊中,或者可能在 <VirtualHost> 區塊中。像這樣的行就可以搞定

Script PUT /put.php

這會告知 Apache 將所有符合您放置此行的環境的 URI 的 PUT 請求傳送至 put.php 指令碼。當然,這假設您已針對 .php 副檔名啟用 PHP,並且 PHP 處於啟用狀態。所有傳送到此指令碼的 PUT 請求的目的資源必須是指令碼本身,而不是上傳檔案應該有的檔案名稱。

然後,您可以使用 PHP 在您的 put.php 中執行類似以下的操作。這會將上傳檔案的內容複製到伺服器上的檔案 myputfile.ext。您可能需要在執行此檔案複製之前執行一些檢查和/或驗證使用者身分。

範例 #1 儲存 HTTP PUT 檔案

<?php
/* PUT 資料透過 stdin 資料流進入 */
$putdata = fopen("php://input", "r");

/* 開啟檔案以供寫入 */
$fp = fopen("myputfile.ext", "w");

/* 一次讀取 1 KB 的資料
並寫入檔案 */
while ($data = fread($putdata, 1024))
fwrite($fp, $data);

/* 關閉資料流 */
fclose($fp);
fclose($putdata);
?>

新增註解

使用者貢獻的註解 6 則註解

37
micronix at gmx dot net
14 年前
您好,PHP 世界,經過幾個小時的擔憂 :=)

我找到了恢復或暫停上傳的解決方案
在此程式碼片段中,這是伺服器端,而不是任何桌面程式上的用戶端,您必須使用位元組範圍來計算已上傳的位元組和遺失的總位元組。

以下是 PHP 程式碼

<?php
$CHUNK
= 8192;

try {
if (!(
$putData = fopen("php://input", "r")))
throw new
Exception("無法取得 PUT 資料。");

// 現在參數可以像其他變數一樣使用
// 請看下方,輸入完成後

$tot_write = 0;
$tmpFileName = "/var/dev/tmp/PUT_FILE";
// 建立暫存檔
if (!is_file($tmpFileName)) {
fclose(fopen($tmpFileName, "x")); //建立檔案並關閉
// 開啟檔案進行寫入
if (!($fp = fopen($tmpFileName, "w")))
throw new
Exception("無法寫入暫存檔");

// 一次讀取一部分資料並寫入檔案
while ($data = fread($putData, $CHUNK)) {
$chunk_read = strlen($data);
if ((
$block_write = fwrite($fp, $data)) != $chunk_read)
throw new
Exception("無法寫入更多到暫存檔");

$tot_write += $block_write;
}

if (!
fclose($fp))
throw new
Exception("無法關閉暫存檔");

unset(
$putData);
} else {
// 開啟檔案進行寫入
if (!($fp = fopen($tmpFileName, "a")))
throw new
Exception("無法寫入暫存檔");

// 一次讀取一部分資料並寫入檔案
while ($data = fread($putData, $CHUNK)) {
$chunk_read = strlen($data);
if ((
$block_write = fwrite($fp, $data)) != $chunk_read)
throw new
Exception("無法寫入更多到暫存檔");

$tot_write += $block_write;
}

if (!
fclose($fp))
throw new
Exception("無法關閉暫存檔");

unset(
$putData);
}

// 檢查檔案長度和 MD5
if ($tot_write != $file_size)
throw new
Exception("檔案大小錯誤");

$md5_arr = explode(' ', exec("md5sum $tmpFileName"));
$md5 = $md5sum_arr[0];
if (
$md5 != $md5sum)
throw new
Exception("MD5 錯誤");
} catch (
Exception $e) {
echo
'', $e->getMessage(), "\n";
}
?>
5
arnaud at caramia dot Fr
1 年前
我們使用 https://pecl.php.net/package/apfd. 解決了我們的問題。

它可以解析使用 PUT 和 PATCH HTTP 請求的 multipart/form-data 內容(檔案和酬載),這在之前只能透過 POST HTTP 請求實現。
2
Oscar Fernandez Sierra
2 年前
這是我成功的方法。網路上有很多範例都無法運作。我在 https://lornajane.net/posts/2009/putting-data-fields-with-php-curl. 找到了這個。

重要:你不應該使用這個程式碼

curl_setopt($ch, CURLOPT_PUT, true);

即使它看起來是正確的選項(對於使用 CURLOPT_POST 的 POST 請求來說,這會是正確的選項,但對於 PUT 請求來說,它無法運作)。

請注意,使用了常數 CURLOPT_CUSTOMREQUEST 來取代 CURLOPT_PUT,並且使用的值是 "PUT" 而不是 true。

<?php

$url
= "....."; // 在這裡放入你的 URL

$data = array("a" => $a);

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));

$response = curl_exec($ch);
if ( !
$response) {
return
false;
}
1
polygon dot co dot in at gmail dot com
1 年前
我對於使用 PUT 方法上傳檔案感到困惑。
我的疑問是為什麼我們不能使用 PUT 方法透過串流上傳多個檔案
PUT 資料會進入 stdin 串流
$putdata = fopen("php://input", "r");
請注意,$putdata 是一個指向正在上傳的檔案內容的檔案指標。
資料會在伺服器端即時接收(也就是說,會在接收到時就可使用)

其次,當我們使用 parse_str(file_get_contents("php://input")) 時。
這表示資料完全在伺服器端接收完畢,然後才提供給腳本使用。

當使用 fopen() 時,無法解析資料。這可以用於上傳大型檔案。
檔案大小可能從數百 MB 到數 GB,其中串流扮演著重要的角色。

串流會以區塊方式將檔案資料提供給腳本,而不是先儲存在暫存資料夾中。
因此,當使用 $putdata = fopen("php://input", "r"); 時,也無法傳遞酬載。
如果有人想要傳遞酬載,唯一的選擇是在 URL 查詢字串中。
6
San
10 年前
與其使用 fread fwrite 來將上傳的內容儲存到檔案。
stream_copy_to_stream 清潔多了。
1
willy at kochkonsult dot no
3 年前
我發現所有使用 PHP 進行 PUT 的範例程式碼,都總是使用預設的硬編碼檔案副檔名來處理傳入的串流。

從傳入的檔案 PUT 請求中,找不到檔案名稱(至少我找不到),但可以在 $_SERVER 全域變數中找到 mimetype。

我使用這段程式碼來取得正確的檔案副檔名

$mimeType = $_SERVER['HTTP_CONTENT_TYPE'];

if ($mimeType!='application/pdf')
{
header('HTTP/1.1 405 只允許 PDF 檔案');
echo("只允許上傳 PDF 檔案 - 此檔案為 ".$mimeType);
die();
}
else $fileExtension = 'pdf';

如果你有可用的 Apache Tika 伺服器,那會是分析檔案內容以取得 mimetype 的最佳選擇,但這可能不是每個人都能使用的選項 :-)
To Top