正文
如果POST中,我们的数据以 Content-Type:application/json;charset=utf-8
的方式传递,那多维数组没有问题,但如果设计到文件传输,
就只能有Content-Type:multipart/form-data; boundary=---------------------------243605601316575381459540562
的方式传递文件流。
但multipart/form-data
方式中单个post字段不支持数组,我们要进行封装。
Curl封装类
<?php
class Curl
{
public static function docurlFile($url, $options = [], $params = [], $files = [])
{
$options = [
CURLOPT_URL => $url,
CURLOPT_TIMEOUT => 30,
CURLOPT_CONNECTTIMEOUT => 30,
CURLOPT_RETURNTRANSFER => true,
] + $options;
if (stripos($url, "https://") !== false) {
$options += [
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1 // ssl2和ssl3等
];
}
$ch = curl_init();
curl_setopt_array($ch, $options);
if (!empty($files)) {
$customeData = self::customePostFields($params, $files);
@curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => implode("\r\n", $customeData['body']),
CURLOPT_HTTPHEADER => [
"Expect: 100-continue",
"Content-Type: multipart/form-data; boundary={$customeData['boundary']}"
]
]);
} else {
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($params)
]);
}
$result = curl_exec($ch);
$status = curl_getinfo($ch);
$curl_errno = curl_errno($ch);
$curl_error = curl_error($ch);
curl_close ($ch);
if (isset($status['http_code']) && $status['http_code'] == 200) {
return $result;
}
return false;
}
private static function customePostFields($params = [], $files = [])
{
// invalid characters for "name" and "filename"
static $disallow = array("\0", "\"", "\r", "\n");
$params = self::postParamsBuild($params);
// build normal parameters
foreach ($params as $k => $v)
{
$k = str_replace($disallow, "_", $k);
$body[] = implode("\r\n", [
"Content-Disposition: form-data; name=\"{$k}\"",
"",
filter_var($v),
]);
}
// build file parameters
foreach ($files as $k => $v)
{
$data = file_get_contents($v['path']);
$mime = isset($v['mime']) ? $v['mime'] : 'application/octet-stream';
$body[] = implode("\r\n", [
"Content-Disposition: form-data; name=\"{$k}\"; filename=\"{$v['name']}\"",
"Content-Type: {$mime}",
"",
$data,
]);
}
// generate safe boundary
do {
$boundary = "---------------------" . md5(mt_rand() . microtime());
} while (preg_grep("/{$boundary}/", $body));
// add boundary for each parameters
array_walk($body, function (&$part) use ($boundary) {
$part = "--{$boundary}\r\n{$part}";
});
// add final boundary
$body[] = "--{$boundary}--";
$body[] = "";
return [
'boundary' => $boundary,
'body' => $body
];
}
/**
* post参数构建,涉及到多维数组
*
*/
private static function postParamsBuild(array $params = [], $numericPrefix = '', $argSeparator = '&', $keyPrefix = '')
{
$arr = array();
foreach ($params as $key => $val)
{
if ($val === NULL) {
continue;
}
if (!is_array($val) && !is_object($val)) {
if (is_bool($val)) {
$val = $val ? 1 : 0;
}
if ($keyPrefix === '') {
if (is_int($key)) {
$arr[$numericPrefix . $key] = $val;
} else {
$arr[$key] = $val;
}
} else {
$arr[$keyPrefix . '[' . $key . ']'] = $val;
}
} else {
if ($keyPrefix === '') {
$newKeyPrefix = $key;
} else {
$newKeyPrefix = $keyPrefix . '[' . $key . ']';
}
$arr += call_user_func_array(__METHOD__, array($val, $numericPrefix, $argSeparator, $newKeyPrefix));
}
}
return $arr;
}
}
提交实现
$postData['name'] = 'abc';
$postData['month'] = 3;
$files = [
'upload' => [
"path" => '/tmp/23232323',
"mime" => "application/octet-stream",
"name" => 'a1.xls'
]
];
$url = 'http://www.ctmdl.com/post/upload';
$ret = Curl::docurlFile($url, [], $postData, $files);
customePostFields() 封装后的数据:
-----------------------------243605601316575381459540562
Content-Disposition: form-data; name="name"
abc
-----------------------------243605601316575381459540562
Content-Disposition: form-data; name="month"
3
-----------------------------243605601316575381459540562
Content-Disposition: form-data; name="upload"; filename="a1.xls"
Content-Type: application/wps-office.xls
(一段文件流乱码,如Ïࡱá À F>