Verbunddaten veröffentlichen
Im Leben eines Programmierers treten Probleme auf, die eine Person fangen. Ich mag die Standardlösung nicht und das war's! Und manchmal kommt es vor, dass Standardlösungen aus irgendeinem Grund nicht funktionieren. Einige Leute umgehen solche Aufgaben, während andere sie gerne lösen. Man kann sogar sagen, dass sie sie selbst finden. Eine dieser Aufgaben ist das Senden einer oder mehrerer Dateien mithilfe der POST-Methode.
Einige werden wahrscheinlich sagen, dass diese Aufgabe überhaupt keine Aufgabe ist. Immerhin gibt es eine wunderbare CURL-Bibliothek, die recht einfach ist und dieses Problem leicht löst! Aber beeil dich nicht. Ja, CURL ist eine leistungsstarke Bibliothek, ja, sie lädt Dateien, aber ... Wie Sie wissen, hat sie eine kleine Funktion - die Datei muss auf Ihrer Festplatte abgelegt werden!
Stellen wir uns nun eine solche Situation vor: Sie generieren dynamisch eine Datei oder sie befindet sich bereits im Speicher und Sie müssen sie per POST-Methode an einen Remote-Webserver senden. Was passiert dann? Müssen Sie es speichern, bevor Sie es senden? Genau das würden 90% der Programmierer tun. Warum nach unnötigen Problemen suchen, wenn die Lösung an der Oberfläche liegt? Aber wir sind nicht bei Ihnen von diesen 90%! Wir sind besser, wir können jedes Problem lösen. Warum brauchen wir eine zusätzliche Aktion? Erstens wird das nicht schnelle Dateisystem der Festplatte verwendet. Zweitens haben wir möglicherweise keinen Zugriff auf das Dateisystem, oder es wird dort zu wenig Speicherplatz zugewiesen.
Wie können wir dieses Problem dann lösen? Dazu müssen Sie sich ansehen, wie die Daten tatsächlich von der POST-Methode übertragen werden. Die einzige Lösung besteht darin, die Datei als zusammengesetzte Anforderung mit zu übertragenmehrteilige / Formulardaten . Diese Technik ist in RFC7578 gut dokumentiert . Lassen Sie uns einen Blick darauf werfen, wie der Hauptteil einer POST-Anforderung mit mehreren Teilen / Formulardaten aussehen wird:
POST /form.html HTTP / 1.1 Host: server.com Referer: http://server.com/form.html User-Agent: Mozilla Inhaltstyp: mehrteilig / Formulardaten; Grenze = ------------- 573cf973d5228 Inhaltslänge: 288 Verbindung: am Leben bleiben Keep-Alive: 300 (leere Zeile) (fehlende Präambel) --------------- 573cf973d5228 Inhaltsdisposition: Formulardaten; name = "Feld" Text --------------- 573cf973d5228 Inhaltsdisposition: Formulardaten; name = "file"; Dateiname = "sample.txt" Inhaltstyp: Text / Klartext Inhaltsdatei --------------- 573cf973d5228--
Unser Körper besteht aus zwei Teilen. Im ersten Teil übergeben wir den Wert des Formulars Feldname = "Feld" gleich: Text . Im zweiten Teil übergeben wir das Feld name = "file" mit dem Inhalt der Datei filename = "sample.txt": Content file . In der Kopfzeile geben wir das Format des Inhalts der POST-Anforderung an - Inhaltstyp: mehrteilig / Formulardaten , die Trennzeichenfolge der Teile: border = ------------- 573cf973d5228 und die Länge der Nachricht - Inhaltslänge : 288 .
Es bleibt in der Tat ein Programm zu schreiben, das diese Methode implementiert. Da wir kluge Leute sind und nicht hundertmal dasselbe in verschiedenen Projekten schreiben, werden wir alles in Form einer Klasse anordnen, die diese Methode implementiert. Erweitern wir es außerdem um verschiedene Optionen zum Senden von Dateien und einfachen Formularelementen. Um das Vorhandensein einer Datei im POST-Datenarray zu unterscheiden, erstellen wir eine separate Datei - einen Container mit dem Inhalt der Datei und ihren Daten (Name und Erweiterung). So wird es also aussehen:
<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>
Nun die Klasse selbst zum Bilden des mehrteiligen / Formulardatenkörpers für die POST-Anforderung:
<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>
Diese Klasse besteht aus mehreren Methoden. Die PartPost-Methode bildet die separaten Teile der zusammengesetzten Anforderung, und die Get-Methode kombiniert diese Teile und bildet den Hauptteil der POST-Anforderung im Format - Multipart / Formulardaten.
Wir haben jetzt eine generische Klasse zum Senden des Hauptteils einer POST-Anfrage. Es bleibt ein Programm zu schreiben, das diese Klasse verwendet, um Dateien an einen Remote-Webserver zu senden. Verwenden wir die CURL-Bibliothek:
// -
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);
Wenn CURL nicht geeignet ist, kann diese Bibliothek zum Senden über Sockets verwendet werden. Nun, eigentlich Links zu Quellen:
- php.net Dokumentationsseite
- CURL- Artikel : POST-Anfrage, zusammengesetzter Inhalt
- wikipedia: mehrteilige / Formulardaten
- RFC7578
Im nächsten Artikel werde ich Ihnen Informationen zum Herunterladen großer Dateien von Remote-Webservern in mehreren Streams mit der angegebenen Geschwindigkeit geben. Vielen Dank an alle, die bis zum Ende gelesen haben, für Ihre Aufmerksamkeit!