POSTリクエスト、複合コンテンツ(マルチパート/フォームデータ)

POSTマルチパート/フォームデータ


複合データの投稿



プログラマーの生活の中で、人を捕まえる問題が発生します。標準のソリューションが気に入らないので、それだけです。また、何らかの理由で標準ソリューションが機能しない場合もあります。そのようなタスクをバイパスする人もいれば、解決したい人もいます。あなたは彼らが彼ら自身を見つけたとさえ言うことができます。そのようなタスクの1つは、POSTメソッドを使用して1つまたは複数のファイルを送信することです。



このタスクはまったくタスクではないと言う人もいるかもしれません。結局のところ、非常にシンプルでこの問題を簡単に解決する素晴らしいCURLライブラリがあります!しかし、急いではいけません。はい、CURLは強力なライブラリです。はい、ファイルをロードしますが...ご存知のように、CURLには小さな機能があります。ファイルはハードドライブに配置する必要があります。



ここで、次の状況を想像してみましょう。動的にファイルを生成しているか、ファイルがすでにメモリ内にあり、POSTメソッドを使用してリモートWebサーバーに送信する必要があります。それではどうなりますか?送信する前に保存する必要がありますか?これはまさにプログラマーの90%が行うことです。解決策が表面にあるのに、なぜ不要な問題を探すのですか?しかし、私たちはこれらの90%からあなたと一緒ではありません!私たちはより良いです、私たちはどんな問題も解決することができます。なぜ追加のアクションが必要なのですか?まず、ハードドライブの高速ではないファイルシステムを使用します。次に、ファイルシステムにアクセスできないか、割り当てられているスペースが少なすぎる可能性があります。



では、どうすればこの問題を解決できますか?これを行うには、POSTメソッドによってデータが実際にどのように送信されるかを確認する必要があります。唯一の解決策は、を使用してファイルを複合リクエストとして転送することです。マルチパート/フォームデータこの手法はRFC7578に詳しく記載されていますマルチパート/フォームデータPOSTリクエストの本文がどのように表示されるかを見てみましょう。

POST /form.html HTTP / 1.1
ホスト:server.com
参照元:http://server.com/form.html
ユーザーエージェント:Mozilla
Content-Type:multipart / form-data; 境界= ------------- 573cf973d5228
コンテンツの長さ:288
接続:キープアライブ
キープアライブ:300
(空の行)
(前文がありません)
--------------- 573cf973d5228
Content-Disposition:form-data; name = "フィールド"

テキスト
--------------- 573cf973d5228
Content-Disposition:form-data; name = "ファイル"; filename = "sample.txt"
コンテンツタイプ:テキスト/プレーン

コンテンツファイル
--------------- 573cf973d5228--






私たちの体は2つの部分で構成されており、最初の部分では、フォームフィールド名=「フィールド」の値を次のように渡しますtext。 2番目の部分では、フィールド名= "file"ファイルの内容filename = "sample.txt":Contentfileを渡します。ヘッダーでは、POSTリクエストのコンテンツの形式を指定します-Content -Type:multipart / form-data、パーツの区切り文字列:boundary = ------------- 573cf973d5228およびメッセージの長さ-Content -Length:288



実際、このメソッドを実装するプログラムを作成することは残っています。私たちは賢い人であり、異なるプロジェクトで同じことを100回書くことはないので、このメソッドを実装するクラスの形ですべてを配置します。さらに、ファイルと単純なフォーム要素の両方を送信するためのさまざまなオプションに拡張してみましょう。また、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要求のマルチパート/フォームデータ本体を形成するためのクラス自体は次のとおりです。



<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サーバーから複数のストリームで指定された速度で大きなファイルをダウンロードする方法について説明します。最後まで読んでくださった皆様、ありがとうございました!



All Articles