webentwicklung-frage-antwort-db.com.de

So erhalten Sie eine Datei über HTTP PUT mit PHP

Dies ist etwas, das mich schon seit einiger Zeit nervt. Ich baue eine RESTful-API auf, die manchmal Dateien empfangen muss.

Bei Verwendung von HTTP POST können wir data from $_POST und files from $_FILES lesen.

Bei Verwendung von HTTP GET können wir data from $_GET und files from $_FILES lesen.

Wenn Sie jedoch HTTP PUT verwenden, ist die einzige Möglichkeit zum Lesen von Daten die Verwendung von php://input stream.

Alles gut und gut, bis ich eine Datei über HTTP PUT senden möchte. Nun funktioniert der php: /Eingabestrom nicht mehr wie erwartet, da er auch eine Datei enthält.

So lese ich aktuell Daten zu einer PUT-Anfrage:

(was gut funktioniert, solange keine Dateien gepostet werden)

$handle  = fopen('php://input', 'r');
$rawData = '';
while ($chunk = fread($handle, 1024)) {
    $rawData .= $chunk;
}

parse_str($rawData, $data);

Wenn ich dann rawData ausstelle, wird das angezeigt

-----ZENDHTTPCLIENT-44cf242ea3173cfa0b97f80c68608c4c
Content-Disposition: form-data; name="image_01"; filename="lorem-ipsum.png"
Content-Type: image/png; charset=binary

�PNG
���...etc etc...
���,
-----ZENDHTTPCLIENT-8e4c65a6678d3ef287a07eb1da6a5380
Content-Disposition: form-data; name="testkey"

testvalue
-----ZENDHTTPCLIENT-8e4c65a6678d3ef287a07eb1da6a5380
Content-Disposition: form-data; name="otherkey"

othervalue

Weiß jemand, wie man Dateien korrekt über HTTP PUT empfängt oder wie man Dateien aus dem Eingabedatenstrom von php: // analysiert?

/ - ===== UPDATE # 1 =====

Ich habe nur die obige Methode ausprobiert. Ich habe keine Ahnung, was ich sonst tun kann.

Ich habe mit dieser Methode keine Fehler erhalten, außerdem bekomme ich nicht das gewünschte Ergebnis der veröffentlichten Daten und Dateien.

===== UPDATE # 2 =====

Ich versende diese Testanforderung mit Zend_Http_Client wie folgt: (Hatte bisher keine Probleme mit Zend_Http_Client)

$client = new Zend_Http_Client();
$client->setConfig(array(
    'strict'       => false,
    'maxredirects' => 0,
    'timeout'      => 30)
);
$client->setUri( 'http://...' );
$client->setMethod(Zend_Http_Client::PUT);
$client->setFileUpload( dirname(__FILE__) . '/files/lorem-ipsum.png', 'image_01');
$client->setParameterPost(array('testkey' => 'testvalue', 'otherkey' => 'othervalue');
$client->setHeaders(array(
    'api_key'    => '...',
    'identity'   => '...',
    'credential' => '...'
));

===== LÖSUNG =====

Es stellte sich heraus, dass ich einige falsche Annahmen gemacht habe, hauptsächlich, dass HTTP PUT dem HTTP POST ähneln würde. Wie Sie unten lesen können, erklärte mir DaveRandom, dass HTTP PUT nicht für die Übertragung mehrerer Dateien in derselben Anfrage gedacht ist.

Ich habe jetzt das Übertragen von Formdaten vom Rumpf in den URL-Querstring verschoben. Der Hauptteil enthält jetzt den Inhalt einer einzelnen Datei.

Weitere Informationen finden Sie in der Antwort von DaveRandom. Es ist episch.

29
Maurice

Die Daten, die Sie anzeigen, stellen keinen gültigen PUT-Anfragetext dar (naja, es könnte , aber ich bezweifle es sehr). Was es zeigt, ist ein multipart/form-data -Anforderungstext - der MIME-Typ, der beim Hochladen von Dateien über HTTP POST über ein HTML-Formular verwendet wird.

PUT-Anforderungen sollten genau die Antwort auf eine GET-Anforderung ergänzen - sie senden Ihnen den Dateiinhalt im Nachrichtentext und sonst nichts.

Im Wesentlichen sage ich, dass es nicht Ihr Code ist, der die falsche Datei empfängt, sondern der Code, der die Anfrage stellt - der Client-Code ist falsch, nicht der Code, den Sie hier anzeigen (obwohl die parse_str() Call ist eine sinnlose Übung).

Wenn Sie erklären, was der Client ist (ein Browser, ein Skript auf einem anderen Server usw.), kann ich Ihnen dabei helfen, dies weiter zu entwickeln. Die geeignete Anforderungsmethode für den von Ihnen dargestellten Anforderungshauptteil ist POST, nicht PUT.


Lassen Sie uns einen Schritt von dem Problem zurücktreten und das HTTP-Protokoll im Allgemeinen - insbesondere die Client-Anforderungsseite - betrachten, damit Sie besser verstehen, wie all dies funktionieren soll. Zuerst ein wenig Geschichte (wenn Sie nicht daran interessiert sind, können Sie diesen Abschnitt überspringen).

Geschichte

HTTP wurde ursprünglich als Mechanismus zum Abrufen von HTML-Dokumenten von Remoteservern entwickelt. Zunächst wurde effektiv nur die Methode GET unterstützt, bei der der Client ein Dokument nach Namen anfordert und der Server es an den Client zurückgibt. Die erste öffentliche Spezifikation für HTTP mit der Bezeichnung HTTP 0.9 wurde 1991 veröffentlicht - und wenn Sie interessiert sind, können Sie sie lesen hier .

Die Spezifikation HTTP 1.0 (1996 mit RFC 1945 formalisiert) erweiterte die Fähigkeiten des Protokolls erheblich und fügte die Methoden HEAD und POST hinzu. Es war nicht abwärtskompatibel mit HTTP 0.9, da das Format der Antwort geändert wurde. Es wurde ein Antwortcode hinzugefügt. Außerdem war es möglich, Metadaten für das zurückgegebene Dokument in Form von MIME-Format-Headern (Schlüssel-/Wertdaten) einzuschließen Paare. HTTP 1.0 abstrahierte das Protokoll auch von HTML und ermöglichte die Übertragung von Dateien und Daten in anderen Formaten.

HTTP 1.1, die Form des Protokolls, das heute fast ausschließlich verwendet wird, baut auf HTTP 1.0 auf und wurde so entwickelt, dass es mit HTTP 1.0-Implementierungen abwärtskompatibel ist. Es wurde 1999 mit RFC 2616 standardisiert. Wenn Sie ein Entwickler sind, der mit HTTP arbeitet, lernen Sie dieses Dokument kennen - es ist Ihre Bibel. Wenn Sie es vollständig verstehen, haben Sie einen erheblichen Vorteil gegenüber Ihren Kollegen, die dies nicht tun.

Komm schon auf den Punkt

HTTP arbeitet mit einer Request-Response-Architektur. Der Client sendet eine Anforderungsnachricht an den Server. Der Server sendet eine Antwortnachricht an den Client.

Eine Anforderungsnachricht enthält eine METHODE, einen URI und optional eine Anzahl von HEADERS. Die Anforderungsmethode ist das, worauf sich diese Frage bezieht. Deshalb werde ich hier ausführlicher darauf eingehen. Zunächst ist es jedoch wichtig zu verstehen, was genau wir meinen, wenn wir über die Anforderungs-URI sprechen.

Der URI ist der Speicherort der angeforderten Ressource auf dem Server. Im Allgemeinen besteht dies aus einer Pfadkomponente und optional einer Abfragezeichenfolge . Es gibt Umstände, unter denen auch andere Komponenten vorhanden sein können, aber der Einfachheit halber werden wir sie vorerst ignorieren.

Stellen Sie sich vor, Sie geben http://server.domain.tld/path/to/document.ext?key=value in die Adressleiste Ihres Browsers ein. Der Browser löst diese Zeichenfolge auf und stellt fest, dass eine Verbindung zu einem HTTP-Server unter server.domain.tld hergestellt werden muss, und fordert das Dokument unter /path/to/document.ext?key=value an.

Die generierte HTTP 1.1-Anforderung sieht (mindestens) so aus:

GET /path/to/document.ext?key=value HTTP/1.1
Host: server.domain.tld

Der erste Teil der Anforderung ist das Wort GET - dies ist die Anforderungsmethode. Der nächste Teil ist der Pfad zu der Datei, die wir anfordern - dies ist der Anforderungs-URI. Am Ende dieser ersten Zeile befindet sich eine Kennung, die die verwendete Protokollversion angibt. In der folgenden Zeile sehen Sie einen Header im MIME-Format mit dem Namen Host. HTTP 1.1 schreibt vor, dass der Header Host: in jeder Anforderung enthalten sein muss. Dies ist der einzige Header, für den dies zutrifft.

Die Anforderungs-URI ist in zwei Teile unterteilt - alles links vom Fragezeichen ? ist der Pfad , alles rechts davon ist der Pfad Abfragezeichenfolge .

Anforderungsmethoden

RFC 2616 (HTTP/1.1) definiert 8 Anforderungsmethoden .

OPTIONS

Die OPTIONS-Methode wird selten verwendet. Es ist als ein Mechanismus gedacht, um zu bestimmen, welche Art von Funktionalität der Server unterstützt, bevor versucht wird, einen Dienst zu nutzen, den der Server möglicherweise bereitstellt.

Ich kann mir nur vorstellen, wo dies häufig verwendet wird, wenn ich Dokumente in Microsoft Office direkt über HTTP vom Internet Explorer aus öffne. Office sendet eine OPTIONS-Anforderung an den Server, um festzustellen, ob dies der Fall ist unterstützt die PUT-Methode für den spezifischen URI. Wenn dies der Fall ist, wird das Dokument so geöffnet, dass der Benutzer seine Änderungen am Dokument direkt auf dem Remoteserver speichern kann. Diese Funktionalität ist eng in diese speziellen Microsoft-Anwendungen integriert.

GET

Dies ist bei weitem die gebräuchlichste Methode im täglichen Gebrauch. Jedes Mal, wenn Sie ein reguläres Dokument in Ihren Webbrowser laden, handelt es sich um eine GET-Anforderung.

Die GET-Methode fordert den Server auf, ein bestimmtes Dokument zurückzugeben. Die einzigen Daten, die an den Server übertragen werden sollten, sind Informationen, die der Server benötigt, um zu bestimmen, welches Dokument zurückgegeben werden soll. Dies kann Informationen enthalten, die der Server zum dynamischen Generieren des Dokuments verwenden kann, das in Form von Headern und/oder Abfragezeichenfolgen in der Anforderungs-URI gesendet wird. Während wir uns mit dem Thema befassen, werden Cookies in den Anforderungsköpfen gesendet.

HEAD

Diese Methode ist mit der GET-Methode identisch, mit dem Unterschied, dass der Server das angeforderte Dokument nicht zurückgibt, sondern nur die Header, die in der Antwort enthalten wären. Dies ist nützlich, um beispielsweise festzustellen, ob ein bestimmtes Dokument vorhanden ist, ohne dass das gesamte Dokument übertragen und verarbeitet werden muss.

POST

Dies ist die zweithäufigste und wohl komplexeste Methode. POST Methodenanforderungen werden fast ausschließlich zum Aufrufen einiger Aktionen auf dem Server verwendet, die möglicherweise seinen Status ändern.

Eine POST -Anforderung kann (und tut dies normalerweise) im Gegensatz zu GET und HEAD einige Daten im Hauptteil der Anforderungsnachricht enthalten. Diese Daten können in einem beliebigen Format vorliegen. In den meisten Fällen handelt es sich jedoch um eine Abfragezeichenfolge (im selben Format wie in der Anforderungs-URI) oder eine mehrteilige Nachricht, die Schlüssel/Wert-Paare zusammen mit Dateianhängen kommunizieren kann.

Viele HTML-Formulare verwenden die Methode POST. Um Dateien von einem Browser hochzuladen, müssten Sie die POST -Methode für Ihr Formular verwenden.

Die POST -Methode ist semantisch inkompatibel mit RESTful-APIs, da sie nicht idempotent ist. Das heißt, eine zweite identische POST -Anforderung kann zu einer weiteren Änderung des Serverstatus führen. Dies widerspricht der "zustandslosen" Einschränkung von REST.

PUT

Dies ergänzt direkt GET. Wenn eine GET-Anforderung angibt, dass der Server das Dokument an dem von der Anforderungs-URI im Antworttext angegebenen Ort zurückgeben soll, gibt die PUT-Methode an, dass der Server die Daten im Anforderungstext an dem von der Anforderungs-URI angegebenen Ort speichern soll.

DELETE

Dies weist darauf hin, dass der Server das Dokument an dem in der Anforderungs-URI angegebenen Speicherort löschen soll. Sehr wenige Internet-konforme HTTP-Server-Implementierungen führen aus ziemlich offensichtlichen Gründen eine Aktion aus, wenn sie eine DELETE-Anforderung erhalten.

TRACE

Dadurch wird ein Mechanismus auf Anwendungsebene bereitgestellt, mit dem Clients die gesendete Anforderung bis zum Erreichen des Zielservers so prüfen können, wie sie aussieht. Dies ist vor allem hilfreich, um festzustellen, welche Auswirkungen Proxy-Server zwischen dem Client und dem Zielserver auf die Anforderungsnachricht haben können.

CONNECT

HTTP 1.1 reserviert den Namen für eine CONNECT-Methode, definiert jedoch weder ihre Verwendung noch ihren Zweck. Einige Proxy-Server-Implementierungen haben seitdem die CONNECT-Methode verwendet, um das HTTP-Tunneling zu vereinfachen.

42
DaveRandom

Ich habe es noch nie mit PUT versucht (GET POST und FILES reichten für meine Bedürfnisse aus), aber dieses Beispiel stammt aus den Php-Dokumenten und könnte Ihnen helfen (http://php.net/manual/de/features). file-upload.put-method.php):

<?php
/* PUT data comes in on the stdin stream */
$putdata = fopen("php://input", "r");

/* Open a file for writing */
$fp = fopen("myputfile.ext", "w");

/* Read the data 1 KB at a time
   and write to the file */
while ($data = fread($putdata, 1024))
  fwrite($fp, $data);

/* Close the streams */
fclose($fp);
fclose($putdata);
?>
6
kjurkovic

Hier ist die Lösung, die ich für die nützlichste hielt.

$put = array(); parse_str(file_get_contents('php://input'), $put);

$put wird ein Array sein, genau wie Sie es gewohnt sind, in $_POST zu sehen, außer dass Sie jetzt das true REST HTTP-Protokoll verfolgen können.

2
Daniel Sikes

Verwenden Sie POST und fügen Sie einen X-Header ein, um die tatsächliche Methode (in diesem Fall PUT) anzugeben. Normalerweise arbeitet man so um eine Firewall, die keine anderen Methoden als GET und POST zulässt. Deklarieren Sie einfach PHP fehlerhaft (da es sich weigert, mehrteilige PUT-Nutzlasten zu handhaben, IS fehlerhaft), und behandeln Sie es wie eine veraltete/draconische Firewall.

Die Meinungen darüber, was PUT in Bezug auf GET bedeutet, sind genau diese Meinungen. Das HTTP macht keine solche Anforderung. Es wird lediglich "gleichwertig" angegeben. Der Designer muss selbst bestimmen, was "gleichwertig" bedeutet. Wenn Ihr Entwurf einen Upload-PUT für mehrere Dateien akzeptieren kann und eine "äquivalente" Repräsentation für einen nachfolgenden GET für dieselbe Ressource erzeugen kann, ist dies technisch und philosophisch mit den HTTP-Spezifikationen gut und unkompliziert. 

1
user4157069

Folgen Sie einfach dem, was im DOC steht:

<?php
/* PUT data comes in on the stdin stream */
$putdata = fopen("php://input", "r");

/* Open a file for writing */
$fp = fopen("myputfile.ext", "w");

/* Read the data 1 KB at a time
   and write to the file */
while ($data = fread($putdata, 1024))
  fwrite($fp, $data);

/* Close the streams */
fclose($fp);
fclose($putdata);
?>

Dieses sollte die gesamte Datei lesen, die sich im PUT-Stream befindet, und es lokal speichern, dann können Sie damit machen, was Sie möchten.

0
Neal