POST请求,复合内容(多部分/表单数据)

POST多部分/表单数据


过帐复合数据



在任何程序员的生活中,都会遇到困扰人的问题。我不喜欢标准解决方案,仅此而已!有时,由于某种原因,标准解决方案无法正常工作。有些人绕过了此类任务,而另一些人则喜欢解决它们。您甚至可以说他们自己找到了他们。这些任务之一是使用POST方法发送一个文件或多个文件。



有人可能会说这根本不是一项任务。毕竟,有一个很棒的CURL库,它很简单,可以轻松解决此问题!但是不要着急。是的,CURL是一个功能强大的库,是的,它可以加载文件,但是...如您所知,它有一个小功能-文件必须放在硬盘上!



现在,让我们想象一下以下情况,您正在动态生成一个文件,或者该文件已经在内存中,并且需要使用POST方法将其发送到远程Web服务器。那会发生什么呢?发送之前需要保存吗?这正是90%的程序员会做的。如果解决方案浮出水面,为什么还要寻找不必要的问题?但是,这90%的人与我们并不在一起!我们更好,我们可以解决任何问题。为什么我们需要采取额外的措施?首先,它使用硬盘驱动器的非快速文件系统。其次,我们可能无法访问文件系统,或者在那里分配的空间太小。



我们该如何解决这个问题?为此,您需要查看POST方法实际如何传输数据。唯一的解决方案是使用以下命令将文件作为复合请求进行传输multipart / form-dataRFC7578对此技术进行了详细记录让我们看一下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不适合,则此库可用于通过套接字发送。好吧,实际上是链接到资源:





在下一篇文章中,我将为您提供有关如何以指定速度从多个Web流中的远程Web服务器下载大文件的信息。对于读完所有文章的所有人,谢谢您的关注!



All Articles