首页 > 程序开发 > PHP > PHP CURL如何处理上传内存中文件,避免磁盘IO开销
2014
12-23

PHP CURL如何处理上传内存中文件,避免磁盘IO开销

普通的CURL上传磁盘文件的方式

发送方send.php的代码如下:

<?php
$target_url ="http://localhost/upload.php";
$filename = realpath("test.txt");
/*
 * 第一种写法,但是在5.5以上版本不推荐使用
 * @$filename 是文件路径,必须有
 * filename=test.txt 是接收方收到的文件名,为空时 则取 filename 文件路径中的 basename部分
 * type=text/plain 文档类型,可以为空
*/

/*
 * 第二种写法,推荐新版本php中使用
 * CURLFile参数解释
 * @$filename 需要上传的文件,建议使用绝对路径
 * @$mimetype: 默认是 application/octet-stream,此处留空
 * @$postname: 接收方$_FILES数组中的文件名,此处为 test.txt
*/
$file = new CURLFile($filename, '', 'test.txt');
$post_data = array(
 'extra_info' => '123456',
 'file_contents' => $file,
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

$result = curl_exec($ch);
curl_close($ch);
echo $result;

处理上传文件的代码upload.php示例:

<?php
$upload_dir = realpath('./') . '/';
$uploadfile = $upload_dir . basename($_FILES['file_contents']['name']);
echo '

<

pre>';
if(move_uploaded_file($_FILES['file_contents']['tmp_name'], $uploadfile)) {
 echo 'ok!';
} else {
 echo 'failed!';
}
//调试信息
var_dump($_FILES);
var_dump($_POST);

利用 PUT 方法上传内存中的文件

有些时候脚本产生的临时小文件,利用普通的上传方式,则需要先把文件写入磁盘,再作为文件上传。产生了额外的开销。最好的办法是直接上传。 重新实现send.php代码如下:

<?php
/**
 * php://temp 会在内存量达到预定义的限制后(默认是 2MB)存入临时文件中。
*/
$fh = fopen('php://temp', 'rw+');
$string = 'test';
fwrite($fh, $string);
rewind($fh);
$ch = curl_init('http://localhost/putfile.php');
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 2);
curl_setopt($ch, CURLOPT_PUT, true);
curl_setopt($ch, CURLOPT_INFILE, $fh);
curl_setopt($ch, CURLOPT_INFILESIZE, strlen($string));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$result = curl_exec($ch);
curl_close($ch);
fclose($fh);

没有用php://memory,会报错Warning: curl_setopt(): cannot represent a stream of type MEMORY as a STDIO FILE*暂无好的解决方案 处理上传的文件的脚本也需要修改下:

<?php
//修改自PHP手册中的代码
$putdata = fopen("php://input","r");


$fp = fopen("myputfile.txt","w");


while ($data = fread($putdata, 1024))
 fwrite($fp, $data);


fclose($fp);
fclose($putdata);

这个方法,适合上传小于2MB的文件,否则还是会生成临时文件。当然该参数可以通过php.ini修改

自己构造请求的主体 实现任意大小文件的直接内存上传

通过CURL 上传文件,不管是磁盘文件还是内存中的字符串也好,其实都是基于HTTP协议的请求。 如果自己构造这段请求,便不再局限于文件的形式了。

<?php
/**
 * 参考rfc1867协议 第6部分的 examples 中的格式 : http://www.ietf.org/rfc/rfc1867.txt
 * 如下:第一行是header 中的(省略了其他header),其他部分是主体部分
Content-type: multipart/form-data, boundary=AaB03x

--AaB03x
content-disposition: form-data; name="field1"

Joe Blow
--AaB03x
content-disposition: form-data; name="pics"; filename="file1.txt"
Content-Type: text/plain

 ... contents of file1.txt ...
--AaB03x--
 * 或
 Content-type: multipart/form-data, boundary=AaB03x

--AaB03x
content-disposition: form-data; name="field1"

Joe Blow
--AaB03x
content-disposition: form-data; name="pics"
Content-type: multipart/mixed, boundary=BbC04y

--BbC04y
Content-disposition: attachment; filename="file1.txt"
Content-Type: text/plain

... contents of file1.txt ...
--BbC04y
Content-disposition: attachment; filename="file2.gif"
Content-type: image/gif
Content-Transfer-Encoding: binary

...contents of file2.gif...
--BbC04y--
--AaB03x--
 * CURL POST,对 CURLOPT_POSTFIELDS 的设置,倘若是字符串可以解释为主体部分
*/
//生成分隔符
$delimiter = '-------------' . uniqid();
//需要上传的文件数组
$fileFields = array(
 'file1' => array(
 'name' => 'test1.txt',
 'type' => 'text/plain',
 'content' => '...this is my file content...'
),
 'file2' => array(
 'name' => 'test.txt',
 'type' => 'text/plain',
 'content' => '... this is my two file'
),
);
//后端接受的$_POST的数组值
$postFields = array(
 'myname' => 'joe',
);
//@var $data 保存主体的字符串
$data = '';

//先将post的普通数据生成主体字符串
foreach ($postFields as $name => $content) {
 $data .="--". $delimiter ."rn";
 $data .= 'Content-Disposition: form-data; name="' . $name . '"';
 //multipart/form-data 不需要urlencode,参见 http:stackoverflow.com/questions/6603928/should-i-url-encode-post-data
 $data .="rnrn". $content ."rn";
}
//将上传的文件生成主体字符串
foreach ($fileFields as $name => $file) {
 $data .="--". $delimiter ."rn";
 $data .= 'Content-Disposition: form-data; name="' . $name . '"; filename="' . $file['name'] .""rn";
 $data .= 'Content-Type: ' . $file['type'] ."rnrn";//多了个文档类型

 $data .= $file['content'] ."rn";
}
//主体结束的分隔符
$data .="--". $delimiter ."--";

$target_url ="http://localhost/upload.php";
$handle = curl_init($target_url);
curl_setopt($handle, CURLOPT_POST, true);
curl_setopt($handle, CURLOPT_HTTPHEADER , array(
 'Content-Type: multipart/form-data; boundary=' . $delimiter,
 'Content-Length: ' . strlen($data))
);
curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($handle, CURLOPT_POSTFIELDS, $data);
$result = curl_exec($handle);
curl_close($handle);
//echo $result;

这种方式实现稍显复杂,但不需要更改处理上传的代码,跟第一种磁盘文件的方法一样。

最后编辑日期:
作者:uuling
这个作者貌似有点懒,什么都没有留下。

留下一个回复

你的email不会被公开。