使用PHP和CURL如何使用多维POSTFIELDS上传文件(multipart/form-data)?

7

我遇到了使用PHP和CURL上传文件时,发布多维数组的问题。

例如,这个多维数组是:

$post['question'] = 'Are you human?';
$post['answers'] = array('yes', 'no', 'maybe');
$post['file'] = '@/path/to/file';

// Output:

Array(
    'question' => Are you human?,
    'answers' => Array(
        '0' => yes,
        '1' => no,
        '2' => maybe
        ),
    'file' => @/path/to/file
)

如果您尝试使用CURL中的CURLOPT_POSTFIELDS来发布此内容,则有几个原因为什么这样不起作用:

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'http://example.com');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);

$response = curl_exec($ch);

首先,PHP官方对CURLOPT_POSTFIELDS的描述如下:
要在HTTP "POST"操作中发布的完整数据。要发布文件,请使用@将文件名前缀并使用完整路径。这可以作为urlencode字符串传递,例如“para1=val1&para2=val2&...”,也可以作为具有字段名称为键和字段数据为值的数组传递。如果值是一个数组,则Content-Type标头将设置为multipart / form-data。
听起来您可以将任何类型的数组传递给POSTFIELDS?错了。POSTFIELDS仅接受非标量值,并且在传递多维数组时会出现“Array to string conversion”错误。因此,您唯一的选择是使用http_build_query()构建您的数组,以便能够传递不会噎住的多维数组。
但是,正如您可以在PHP页面上的注释中读到的那样:
注意:将数组传递给CURLOPT_POSTFIELDS将将数据编码为multipart / form-data,而将URL编码的字符串传递将将数据编码为application / x-www-form-urlencoded。
如果您向POSTFIELDS传递urlencode字符串,则无法将帖子编码为multipart / form-data,从而导致文件上传失败。
因此,似乎几乎不可能在CURL中将两者组合起来,而如果您使用常规HTML表单,则不会有问题。
我的问题是:是否可以绕过CURL的这种奇怪行为,以便能够发布多维数组和文件上传?
3个回答

6

multipart/form-data不支持嵌套值。我认为CURL也不能做到。

我怀疑你请求的接收端也是一个PHP脚本。如果是这样,你可以自己准备一个嵌套数组作为其中一个值进行提交:

 $post['answers[0]'] = "yes";
 $post['answers[1]'] = "no";
 $post['answers[2]'] = "maybe";

理论上,您只需要使用'answers[]'而不带索引,但这将覆盖前面的值 - 因此只能与http_build_query一起使用。
我不确定PHP中是否有任何可以自动执行此操作的HTTP库。

2

实现第一个答案的另一种方法:

foreach( array("yes","no","maybe") as $key=>$value )
    $post["answers[$key]"] = $value;

1
尝试使用这个递归函数。

https://gist.github.com/yisraeldov/ec29d520062575c204be7ab71d3ecd2f

<?php
function build_post_fields( $data,$existingKeys='',&$returnArray=[]){
    if(($data instanceof CURLFile) or !(is_array($data) or is_object($data))){
        $returnArray[$existingKeys]=$data;
        return $returnArray;
    }
    else{
        foreach ($data as $key => $item) {
            build_post_fields($item,$existingKeys?$existingKeys."[$key]":$key,$returnArray);
        }
        return $returnArray;
    }
}

你可以像这样使用它。
curl_setopt($ch, CURLOPT_POSTFIELDS, build_post_fields($postfields));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接