使用cURL和PHP为相同的键和文件上载混合多个值

时间:2008-12-31 10:26:12

标签: php curl

我在PHP的cURL绑定中遇到了限制。似乎没有简单的方法为postfields发送相同的多个值。我遇到的大多数解决方法都涉及手工创建URL编码的帖子字段tag = foo& tag = bar& tag = baz),而不是使用CURLOPT_POSTFIELDS的关联数组版本。

这似乎是一个非常普遍的事情需要支持所以我觉得我一定错过了什么。这真的是处理同一个键的多个值的唯一方法吗?

虽然这种解决方法可能被认为是可行的(如果不是真的很烦人),但我的主要问题是我需要能够为同一个键执行多个值并支持文件上载。据我所知,文件上传或多或少需要使用CURLOPT_POSTFIELDS的关联arravy版本。所以我觉得我被卡住了。

我在cURL PHP邮件列表上有更详细的问题posted,希望有人对此有所了解。

非常感谢有关我可以在哪里寻找更多相关信息的建议或提示!

10 个答案:

答案 0 :(得分:11)

我最终编写了自己的函数来构建一个带有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堆栈,所以由于这个方法调用它,我确定该函数允许用户在需要时指定附加标题。

我有完整的代码in this blog post

答案 1 :(得分:3)

如果您使用tag[]而不是tag作为名称{@ 3}},换句话说,而不是

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

答案 2 :(得分:2)

我使用了它:

curl_setopt($ch, CURLOPT_POSTFIELDS,array('tag[0]'=>'val0','tag[1]'=>'val1'));

然后$_POST会产生:$_POST['tag'][0] = 'val0'$_POST['tag'][1] = 'val1'

答案 3 :(得分:2)

我遇到了同样的问题。但我能够以这种方式解决它。

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;
}

答案 4 :(得分:1)

我认为一个键(或同一个键)中多个值的既定标准是将其与分隔符连接,例如对表单元素中的多个选项列表进行选择。我相信这个分隔符是制表符(\t)或管道符号(|)。

如果键名以[]终止(如tag[]),则PHP会自动将值转换为数组,以方便您使用。

答案 5 :(得分:1)

lImbus和paul,谢谢你的意见。

如果我控制了我发布的表单,我可能会找到解决此问题的替代方法。但是,我对表格没有任何控制权。我几乎肯定读取帖子的软件不是PHP,并且不遵守标签[]标准。

即使它确实如此,cURL似乎也不遵守标签[]语法。基本上,我尝试了以下,但都没有工作......

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 []'。

我真的开始觉得cURL PHP绑定真的不支持这个。这对我来说似乎很令人惊讶。看起来它可以做任何其他事情,但它无法做这样简单的事情吗?

答案 6 :(得分:0)

我在网上找到了这个答案,想要在它消失之前将其发布在这里:

http://yeehuichan.wordpress.com/2011/08/07/sending-multiple-values-with-the-same-namekey-in-curl-post/

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'),
));

答案 7 :(得分:0)

  1. Postgresql: Conditionally unique constraint投票。
  2. 尝试@BeauSimensen的答案。
  3. PHP Bug #51634可以做到这一点。请参见下面的示例。
$client = new \GuzzleHttp\Client();
$client->request('POST', $url, [
  'multipart' => [
    [ 'name' => 'foo', 'contents' => 'bar' ],
    [ 'name' => 'foo', 'contents' => 'baz' ],
  ]
]);

答案 8 :(得分:0)

不要使用迷宫

# 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命令的发布字段:

$fields = array('key1' => 'value1', 'var' => array(1,2,3,4));
$curlPost = \http_build_query($fields);
curl_setopt($ch, CURLOPT_POSTFIELDS, $curlPost);

那是三行代码! Guzzle中有几千条代码行? (*)

到目前为止,我使用curl

那用大约100条线代替了百万条线!

(*): http_build_query的结果可以根据您的需要进行进一步格式化。

答案 9 :(得分:0)

我遇到了同样的问题,我不得不将一个必须是数组的参数从 PHP 服务器发送到另一台不使用“[]”将具有相同键的值与文件混合的服务器。 在 Laravel 8 中,我可以通过 {{3}} 实现这个目标(当然 Http 客户端使用 guzzle)。 这是我的代码示例。

Illuminate\Support\Facades\Http::attach('file', $fileContents, 'file-name')
->post('https://destination' , [['name' => 'tag', 'content' => 'foo'], ['name' => 'tag', 'content' => 'bar']])