[英]Mixing multiple values for the same key and file uploads using cURL and PHP
我在 PHP 的 cURL 綁定中遇到了限制。 似乎沒有簡單的方法可以為 postfields 的相同鍵發送相同的多個值。 我遇到的大多數解決方法都涉及手動創建 URL 編碼的帖子字段 tag=foo&tag=bar&tag=baz) 而不是使用 CURLOPT_POSTFIELDS 的關聯數組版本。
需要支持似乎是一件很常見的事情,所以我覺得我一定錯過了一些東西。 這真的是處理同一個鍵的多個值的唯一方法嗎?
雖然這種解決方法可能被認為是可行的(如果不是很煩人的話),我的主要問題是我需要能夠為同一個鍵設置多個值並且還支持文件上傳。 據我所知,文件上傳或多或少需要使用 CURLOPT_POSTFIELDS 的關聯數組版本。 所以我覺得我被卡住了。
我已經在 cURL PHP 郵件列表上更詳細地發布了這個問題,希望有人對此有一些想法。
非常感謝我可以在哪里尋找更多信息的建議或提示!
我最終編寫了自己的函數來構建一個帶有 multipart/form-data 的自定義 CURLOPT_POSTFIELDS 字符串。 多么痛苦。
function curl_setopt_custom_postfields($ch, $postfields, $headers = null) {
// $postfields is an assoc array.
// Creates a boundary.
// Reads each postfields, detects which are @files, and which values are arrays
// and dumps them into a new array (not an assoc array) so each key can exist
// multiple times.
// Sets content-length, content-type and sets CURLOPT_POSTFIELDS with the
// generated body.
}
我能夠像這樣使用這種方法:
curl_setopt_custom_postfields($ch, array(
'file' => '@/path/to/file',
'tag' => array('a', 'b', 'c'),
));
我不確定 CURLOPT_HTTPHEADER 堆棧,因此由於此方法調用它,我確定該函數將允許用戶在需要時指定附加標頭。
我在這篇博文中提供了完整的代碼。
如果您使用tag[]
而不是tag
作為名稱, PHP 將為您生成一個數組,換句話說,而不是
tag=foo&tag=bar&tag=baz
你需要
tag[]=foo&tag[]=bar&tag[]=baz
請注意,當 urlencoded 用於傳輸時,這應該變成
tag%5B%5D=foo&tag%5B%5D=bar&tag%5B%5D=baz
我使用它工作:
curl_setopt($ch, CURLOPT_POSTFIELDS,array('tag[0]'=>'val0','tag[1]'=>'val1'));
然后$_POST
結果為: $_POST['tag'][0] = 'val0'
和$_POST['tag'][1] = 'val1'
我遇到了同樣的問題。 但我能夠通過這種方式解決它。
for($cnt = 0; $cnt < count($siteRows); $cnt++)
{
$curlParams['site_ids['.$cnt.']'] = $siteRows[$cnt]->site_id;
}
也適用於文件:
for($cnt = 0; $cnt < count($imageRows); $cnt++)
{
$curlParams['product_images['.$cnt.']'] = '@'.$imageRows[$cnt]->full_path;
}
$client = new \GuzzleHttp\Client();
$client->request('POST', $url, [
'multipart' => [
[ 'name' => 'foo', 'contents' => 'bar' ],
[ 'name' => 'foo', 'contents' => 'baz' ],
]
]);
我認為一個鍵(或同一個鍵)中的多個值的既定標准是將它與分隔符連接,例如表單元素中的多個選項列表。 我相信這個分隔符是制表符( \\t
)或管道符號( |
)。
如果鍵名以[]
結尾(如tag[]
),PHP 會自動將值轉換為數組以方便您使用。
lImbus 和 paul,感謝您的投入。
如果我可以控制我要發布到的表單,我可能會找到解決此問題的替代解決方案。 但是,我對表單沒有任何控制權。 而且我幾乎肯定閱讀帖子的軟件不是 PHP 並且不遵守 tag[] 標准。
即使這樣做了,cURL 似乎也不遵守 tag[] 語法。 基本上,我嘗試了以下方法,但都沒有奏效......
curl_setopt($ch, CURLOPT_POSTFIELDS, array('file' => '@/pathtofile', 'tag[]' => array('a', 'b', 'c'));
curl_setopt($ch, CURLOPT_POSTFIELDS, array('file' => '@/pathtofile', 'tag' => array('a', 'b', 'c'));
再說一次,我不認為傳遞 tag[] 會起作用,因為我發布的表單實際上是在尋找“tag”而不是“tag[]”。
我真的開始覺得 cURL PHP 綁定真的不支持這個。 這對我來說似乎太令人驚訝了。 看起來它可以做任何其他事情,但它無法做這樣簡單的事情?
不要使用 GUZZLE :
# at your command line start php interactive
user@group:~:php -a
php > $arr=array('var' => array(1,2,3,4));
php > echo http_build_query($arr);
var%5B0%5D=1&var%5B1%5D=2&var%5B2%5D=3&var%5B3%5D=4
php > echo urldecode(http_build_query($arr));
var[0]=1&var[1]=2&var[2]=3&var[3]=4
因此,您需要在http_build_query中傳遞鍵值的哈希數組; 您的(數組)變量作為鍵輸入,其值為數組而不是標量值,如'var' => array(1,2,3,4)
。 現在, http_build_query
可以格式化curl
命令的 post 字段:
$fields = array('key1' => 'value1', 'var' => array(1,2,3,4));
$curlPost = \http_build_query($fields);
curl_setopt($ch, CURLOPT_POSTFIELDS, $curlPost);
這是3行代碼! Guzzle 中有多少 1000 行代碼? (*)
到目前為止,我使用curl
來:
那是用大約 100 行替換數百萬行!
(*): http_build_query
的結果可以根據您的需要進一步格式化。
我遇到了同樣的問題,我必須將一個參數從 PHP 服務器發送到另一個服務器,該參數必須是一個數組,該服務器不使用“[]”將具有相同鍵的值與文件混合在一起。 在 Laravel 8 中,我可以使用Http 客戶端(當然 Http 客戶端使用 guzzle)來實現這個目標。 這是我的代碼示例。
Illuminate\Support\Facades\Http::attach('file', $fileContents, 'file-name')
->post('https://destination' , [['name' => 'tag', 'content' => 'foo'], ['name' => 'tag', 'content' => 'bar']])
我在網上找到了這個答案,想在它消失之前把它貼在這里:
function curl_setopt_custom_postfields($ch, $postfields, $headers = null) {
$algos = hash_algos();
$hashAlgo = null;
foreach ( array('sha1', 'md5') as $preferred ) {
if ( in_array($preferred, $algos) ) {
$hashAlgo = $preferred;
break;
}
}
if ( $hashAlgo === null ) { list($hashAlgo) = $algos; }
$boundary =
'----------------------------' .
substr(hash($hashAlgo, 'cURL-php-multiple-value-same-key-support' . microtime()), 0, 12);
$body = array();
$crlf = "\r\n";
$fields = array();
foreach ( $postfields as $key => $value ) {
if ( is_array($value) ) {
foreach ( $value as $v ) {
$fields[] = array($key, $v);
}
} else {
$fields[] = array($key, $value);
}
}
foreach ( $fields as $field ) {
list($key, $value) = $field;
if ( strpos($value, '@') === 0 ) {
preg_match('/^@(.*?)$/', $value, $matches);
list($dummy, $filename) = $matches;
$body[] = '--' . $boundary;
$body[] = 'Content-Disposition: form-data; name="' . $key . '"; filename="' . basename($filename) . '"';
$body[] = 'Content-Type: application/octet-stream';
$body[] = '';
$body[] = file_get_contents($filename);
} else {
$body[] = '--' . $boundary;
$body[] = 'Content-Disposition: form-data; name="' . $key . '"';
$body[] = '';
$body[] = $value;
}
}
$body[] = '--' . $boundary . '--';
$body[] = '';
$contentType = 'multipart/form-data; boundary=' . $boundary;
$content = join($crlf, $body);
$contentLength = strlen($content);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Length: ' . $contentLength,
'Expect: 100-continue',
'Content-Type: ' . $contentType,
));
curl_setopt($ch, CURLOPT_POSTFIELDS, $content);
}
並使用它:
curl_setopt_custom_postfields($ch, array(
'file' => '@a.csv',
'name' => array('James', 'Peter', 'Richard'),
));
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.