过帐复合数据
在任何程序员的生活中,都会遇到困扰人的问题。我不喜欢标准解决方案,仅此而已!有时,由于某种原因,标准解决方案无法正常工作。有些人绕过了此类任务,而另一些人则喜欢解决它们。您甚至可以说他们自己找到了他们。这些任务之一是使用POST方法发送一个文件或多个文件。
有人可能会说这根本不是一项任务。毕竟,有一个很棒的CURL库,它很简单,可以轻松解决此问题!但是不要着急。是的,CURL是一个功能强大的库,是的,它可以加载文件,但是...如您所知,它有一个小功能-文件必须放在硬盘上!
现在,让我们想象一下以下情况,您正在动态生成一个文件,或者该文件已经在内存中,并且需要使用POST方法将其发送到远程Web服务器。那会发生什么呢?发送之前需要保存吗?这正是90%的程序员会做的。如果解决方案浮出水面,为什么还要寻找不必要的问题?但是,这90%的人与我们并不在一起!我们更好,我们可以解决任何问题。为什么我们需要采取额外的措施?首先,它使用硬盘驱动器的非快速文件系统。其次,我们可能无法访问文件系统,或者在那里分配的空间太小。
我们该如何解决这个问题?为此,您需要查看POST方法实际如何传输数据。唯一的解决方案是使用以下命令将文件作为复合请求进行传输multipart / form-data。RFC7578中对此技术进行了详细记录。让我们看一下multipart / form-data POST请求的主体如何:
POST /form.html HTTP / 1.1 主机:server.com 引荐来源:http://server.com/form.html 用户代理:Mozilla 内容类型:多部分/表单数据;边界= ------------- 573cf973d5228 内容长度:288 连接:保持活动状态 保持生命:300 (空行) (缺少序言) --------------- 573cf973d5228 内容处置:表单数据;名称=“字段” 文本 --------------- 573cf973d5228 内容处置:表单数据;名称=“文件”; 文件名=“ sample.txt” 内容类型:文本/纯文本 内容文件 --------------- 573cf973d5228--
我们的主体由两部分组成,在第一部分中,我们传递形式为field name =“ field”的值,该值等于:text。在第二部分中,我们将字段名称=“ file”传递给文件filename =“ sample.txt”的内容:Content file。在标头中,我们指定POST请求内容的格式-Content -Type:multipart / form-data,部分的分隔符字符串:boundary = ------------- 573cf973d5228和消息的长度-Content -Length:288。
实际上,仍然需要编写实现此方法的程序。由于我们是聪明人,不会在不同的项目中写一百遍相同的东西,因此我们将以实现此方法的类的形式来安排所有内容。另外,让我们将其扩展为发送文件和简单表单元素的不同选项。为了区分POST数据数组中文件的存在,让我们创建一个单独的文件-一个包含文件内容及其数据(名称和扩展名)的容器。因此,它将如下所示:
<pre>
class oFile
{
private $name;
private $mime;
private $content;
public function __construct($name, $mime=null, $content=null)
{
// , $content=null, $name -
if(is_null($content))
{
// (, )
$info = pathinfo($name);
//
if(!empty($info['basename']) && is_readable($name))
{
$this->name = $info['basename'];
// MIME
$this->mime = mime_content_type($name);
//
$content = file_get_contents($name);
//
if($content!==false) $this->content = $content;
else throw new Exception('Don`t get content - "'.$name.'"');
} else throw new Exception('Error param');
} else
{
//
$this->name = $name;
// MIME
if(is_null($mime)) $mime = mime_content_type($name);
// MIME
$this->mime = $mime;
//
$this->content = $content;
};
}
//
public function Name() { return $this->name; }
// MIME
public function Mime() { return $this->mime; }
//
public function Content() { return $this->content; }
};
</pre>
现在,用于为POST请求形成multipart / form-data主体的类本身:
<pre>
class BodyPost
{
//
public static function PartPost($name, $val)
{
$body = 'Content-Disposition: form-data; name="' . $name . '"';
// oFile
if($val instanceof oFile)
{
//
$file = $val->Name();
// MIME
$mime = $val->Mime();
//
$cont = $val->Content();
$body .= '; filename="' . $file . '"' . "\r\n";
$body .= 'Content-Type: ' . $mime ."\r\n\r\n";
$body .= $cont."\r\n";
} else $body .= "\r\n\r\n".urlencode($val)."\r\n";
return $body;
}
// POST
public static function Get(array $post, $delimiter='-------------0123456789')
{
if(is_array($post) && !empty($post))
{
$bool = false;
//
foreach($post as $val) if($val instanceof oFile) {$bool = true; break; };
if($bool)
{
$ret = '';
// , POST
foreach($post as $name=>$val)
$ret .= '--' . $delimiter. "\r\n". self::PartPost($name, $val);
$ret .= "--" . $delimiter . "--\r\n";
} else $ret = http_build_query($post);
} else throw new \Exception('Error input param!');
return $ret;
}
};
</pre>
此类包含几种方法。PartPost方法构成了复合请求的各个部分,而Get方法则将这些部分组合在一起并形成了POST请求的主体,格式为multipart / form-data。
现在,我们有了一个通用类,用于发送POST请求的正文。仍然需要编写一个使用此类将文件发送到远程Web服务器的程序。让我们使用CURL库:
// -
include "ofile.class.php";
// POST
include "bodypost.class.php";
// POST
$delimiter = '-------------'.uniqid();
// oFile
$file = new oFile('sample.txt', 'text/plain', 'Content file');
// POST
$post = BodyPost::Get(array('field'=>'text', 'file'=>$file), $delimiter);
// CURL
$ch = curl_init();
//
curl_setopt($ch, CURLOPT_URL, 'http://server/upload/');
// , POST
curl_setopt($ch, CURLOPT_POST, 1);
// POST
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
/* :
Content-Type - ,
boundary -
Content-Length - */
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: multipart/form-data; boundary=' . $delimiter,
'Content-Length: ' . strlen($post)));
// POST Web
curl_exec($ch);
如果CURL不适合,则此库可用于通过套接字发送。好吧,实际上是链接到资源:
- php.net文档站点
- CURL文章:POST请求,复合内容
- 维基百科:多部分/表单数据
- RFC7578
在下一篇文章中,我将为您提供有关如何以指定速度从多个Web流中的远程Web服务器下载大文件的信息。对于读完所有文章的所有人,谢谢您的关注!