由於項目需要,需要本機伺服器接收資料後,再將資料轉寄到另外一台伺服器上,故要用到類比post請求發送資料,當然資料中也包含檔案流。 curl是php比較常用的方式之一,一般代碼如下:
- $params1 = "test";
- $params2 = "@".$absolute_path;//如果是檔案 則參數為"@"+絕對路徑
- $post_data = array(
- 'params1' => $params1,
- 'params2' => $params2,
- );
- function postData($url, $data){
- $ch = curl_init();
- $timeout = 300;
- curl_setopt($ch, CURLOPT_URL, $url); //請求地址
- //curl_setopt($ch, CURLOPT_REFERER, $ip);//構造來路
- curl_setopt($ch, CURLOPT_POST, true); //post請求
- curl_setopt($ch, CURLOPT_BINARYTRANSFER,true);//二進位流
- curl_setopt($ch, CURLOPT_POSTFIELDS, $data); //資料
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //當CURLOPT_RETURNTRANSFER設定為1時 $head 有請求的傳回值
- curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); //佈建要求逾時時間
- $handles = curl_exec($ch);
- curl_close($ch);
- return $handles;
- }
複製代碼 對方是java伺服器,我只知道介面,並不知道對方如何處理檔案接收的。上面這種方式在win7 wamp環境下是成功的,但是將代碼放到centOS+Nginx伺服器上卻失敗,返回的訊息是檔案接收失敗。經過抓包分析,發現在win7 wamp下發的包和centos nginx下發的http包格式有區別。一般情況下curl預設把content_type設為了multipart/form-data,在我的機器上 win7 wamp下是如此,但是centos nginx下卻是application/x-www-form-urlencoded。當然這也可能是伺服器配置問題,只是我並不知道問題在哪。然後我 又查看了下PHP版本,同是PHP5.3.X,但是有細微差別。也不排除是PHP版本的問題。之後添加代碼:
- $header = array(
- 'Content-Type: multipart/form-data',
- );
- curl_setopt( $ch, CURLOPT_HTTPHEADER, $header);
複製代碼 設定header,但是在centos下依舊無效。居然無法改變content-type,簡直坑爹。 後來在技術總監的協助下,看了PHP官方網站上的一個連結http://php.net/manual/en /class.curlfile.php,參照官網做法在win wamp和centos nginx下post請求都成功了。仔細閱讀了代碼,發現做法竟是完整的書寫了http請求的body部分,而不用curl自己產生的部分,不得不佩服。 下面放出代碼:
- function postData($url, $data = array(), $data1 = array()){
- $header = array(
- 'Content-Type: multipart/form-data',
- );
- $ch = curl_init();
- curl_setopt ($ch, CURLOPT_URL, $url);
- curl_setopt( $ch, CURLOPT_HTTPHEADER, $header);
- curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT,10);
- curl_setopt ($ch, CURLOPT_BINARYTRANSFER,true);
- //curl_setopt ($ch, CURLOPT_POSTFIELDS, $data);
- curl_custom_postfields($ch, $data, $data1);
- $dxycontent = curl_exec($ch);
- curl_close($ch);
- return $dxycontent;
- }
-
- /**
- * For safe multipart POST request for PHP5.3 ~ PHP 5.4.
- *
- * @param resource $ch cURL resource
- * @param array $assoc "name => value"
- * @param array $files "name => path"
- * @return bool
- */
- function curl_custom_postfields($ch, array $assoc = array(), array $files = array()) {
- // invalid characters for "name" and "filename"
- static $disallow = array("\0", "\"", "\r", "\n");
-
- // build normal parameters
- foreach ($assoc as $k => $v) {
- $k = str_replace($disallow, "_", $k);
- $body[] = implode("\r\n", array(
- "Content-Disposition: form-data; name=\"{$k}\"",
- "",
- filter_var($v),
- ));
- }
-
- // build file parameters
- foreach ($files as $k => $v) {
- switch (true) {
- case false === $v = realpath(filter_var($v)):
- case !is_file($v):
- case !is_readable($v):
- continue; // or return false, throw new InvalidArgumentException
- }
- $data = file_get_contents($v);
- $v = call_user_func("end", explode(DIRECTORY_SEPARATOR, $v));
- $k = str_replace($disallow, "_", $k);
- $v = str_replace($disallow, "_", $v);
- $body[] = implode("\r\n", array(
- "Content-Disposition: form-data; name=\"{$k}\"; filename=\"{$v}\"",
- "Content-Type: application/octet-stream",
- "",
- $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[] = "";
-
- // set options
- return @curl_setopt_array($ch, array(
- CURLOPT_POST => true,
- CURLOPT_POSTFIELDS => implode("\r\n", $body),
- CURLOPT_HTTPHEADER => array(
- "Expect: 100-continue",
- "Content-Type: multipart/form-data; boundary={$boundary}", // change Content-Type
- ),
- ));
- }
複製代碼 參數傳遞無影響,若是檔案則在絕對路徑前+"@"。唯一的區別就是將檔案資料和普通資料用不同的數組區分開來,在類比http的body部分時對其進行不同的處理。最終成功上傳檔案。 |