32

これはしばらくの間私を悩ませてきたものです..私は時々ファイルを受信しなければならないRESTfulAPIを構築しています。

を使用すると、とHTTP POSTを読み取ることができます。data from $_POSTfiles from $_FILES

を使用すると、とHTTP GETを読み取ることができます。data from $_GETfiles from $_FILES

ただし、を使用する場合HTTP PUT、AFAIKはデータを読み取る唯一の方法はを使用することphp://input streamです。

HTTP PUTを介してファイルを送信するまでは、すべて順調です。php:// inputストリームにはファイルも含まれているため、期待どおりに機能しなくなりました。

現在、PUTリクエストのデータを読み取る方法は次のとおりです。

(ファイルが投稿されていない限り、これはうまく機能します)

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

parse_str($rawData, $data);

次にrawDataを出力すると、次のように表示されます。

-----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

HTTP PUTを介してファイルを適切に受信する方法、またはphp:// inputストリームからファイルを解析する方法を知っている人はいますか?

=====アップデート#1 =====

私は上記の方法だけを試しましたが、他に何ができるかについての手がかりはありません。

この方法を使用してもエラーは発生しませんが、投稿されたデータとファイルの望ましい結果が得られません。

=====アップデート#2 =====

次のように、Zend_Http_Clientを使用してこのテストリクエストを送信しています:(これまでのところ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' => '...'
));

=====ソリューション=====

主にHTTPPUTはHTTPPOSTに似ているという、いくつかの間違った仮定をしたことがわかりました。以下で読むことができるように、DaveRandomは、HTTPPUTは同じリクエストで複数のファイルを転送するためのものではないことを説明しました。

フォームデータの転送を本文からURLクエリ文字列に移動しました。これで、本文は単一のファイルの内容を保持します。

詳細については、DaveRandomの回答を参照してください。それは叙事詩です。

4

5 に答える 5

44

表示するデータは、有効なPUTリクエストの本文を示していません(まあ、それは可能ですが、私はそれを非常に疑っています)。表示されるのはmultipart/form-dataリクエスト本文です。これは、HTMLフォームを介してHTTPPOST経由でファイルをアップロードするときに使用されるMIMEタイプです。

PUTリクエストは、GETリクエストへの応答を正確に補完する必要があります。つまり、メッセージ本文のファイルの内容だけを送信します。

基本的に私が言っているのは、間違ったファイルを受け取るのはあなたのコードではなく、リクエストを行っているコードです-ここに表示されているコードではなく、クライアントコードが間違っています(parse_str()呼び出しは無意味ですが)エクササイズ)。

クライアントが何であるか(ブラウザ、他のサーバー上のスクリプトなど)を説明していただければ、これをさらに進めるお手伝いをいたします。現状では、描写するリクエスト本文の適切なリクエスト方法は、PUTで​​はなくPOSTです。


問題から一歩後退して、一般的なHTTPプロトコル(特にクライアント要求側)を見てみましょう。これが、これらすべてがどのように機能するかを理解するのに役立つことを願っています。まず、少し歴史があります(これに興味がない場合は、このセクションをスキップしてください)。

歴史

HTTPは元々、リモートサーバーからHTMLドキュメントを取得するためのメカニズムとして設計されました。最初は、GETメソッドのみを効果的にサポートしていました。これにより、クライアントは名前でドキュメントを要求し、サーバーはそれをクライアントに返します。HTTP 0.9というラベルの付いたHTTPの最初の公開仕様は、1991年に登場しました。興味がある場合は、ここで読むことができます。

HTTP 1.0仕様(1996年にRFC 1945で正式化)は、プロトコルの機能を大幅に拡張し、HEADメソッドとPOSTメソッドを追加しました。応答の形式が変更されたため、HTTP 0.9との下位互換性はありませんでした。応答コードが追加され、返されたドキュメントのメタデータをMIME形式のヘッダーの形式で含めることができました。キー/値データペア。HTTP 1.0は、HTMLからプロトコルを抽象化し、他の形式のファイルやデータの転送を可能にしました。

HTTP 1.1は、現在ほとんど独占的に使用されているプロトコルの形式であり、HTTP 1.0の上に構築されており、HTTP1.0実装との下位互換性があるように設計されています。1999年にRFC2616で標準化されました。HTTPを使用する開発者の場合は、このドキュメントを理解してください。これは聖書です。それを完全に理解することは、そうでないあなたの仲間よりもあなたにかなりの利点を与えるでしょう。

すでに要点を理解する

HTTPは要求/応答アーキテクチャで機能します。クライアントは要求メッセージをサーバーに送信し、サーバーは応答メッセージをクライアントに返します。

要求メッセージには、METHOD、URI、およびオプションでいくつかのHEADERSが含まれます。リクエストメソッドはこの質問に関連するものなので、ここで最も詳しく説明しますが、最初に、リクエストURIについて説明するときに意味することを正確に理解することが重要です。

URIは、リクエストしているリソースのサーバー上の場所です。一般に、これはパスコンポーネントと、オプションでクエリ文字列で構成されます。他のコンポーネントも存在する場合がありますが、簡単にするために、ここではそれらを無視します。

http://server.domain.tld/path/to/document.ext?key=valueブラウザのアドレスバーに入力するとします。ブラウザはこの文字列を解体し、でHTTPサーバーに接続する必要があると判断しserver.domain.tld、でドキュメントを要求します/path/to/document.ext?key=value

生成されたHTTP1.1リクエストは、(少なくとも)次のようになります。

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

リクエストの最初の部分は単語ですGET-これはリクエストメソッドです。次の部分は、リクエストしているファイルへのパスです。これはリクエストURIです。この最初の行の終わりには、使用中のプロトコルバージョンを示す識別子があります。次の行に、。と呼ばれるMIME形式のヘッダーが表示されHostます。HTTP 1.1ではHost:、すべてのリクエストにヘッダーを含めることが義務付けられています。これは、これが当てはまる唯一のヘッダーです。

リクエストURIは2つの部分に分かれています。疑問符の左側は?すべてパスであり、右側はすべてクエリ文字列です。

リクエストメソッド

RFC 2616(HTTP / 1.1)は、8つの要求メソッドを定義しています。

OPTIONS

OPTIONSメソッドはめったに使用されません。これは、サーバーが提供する可能性のあるサービスを利用する前に、サーバーがサポートする機能の種類を判別するためのメカニズムとして意図されています。

頭から離れて、これが使用されている場所を考えることができるかなり一般的な使用法の唯一の場所は、InternetExplorerからHTTPを介してMicrosoftOfficeで直接ドキュメントを開くときです-OfficeはサーバーにOPTIONS要求を送信してそれがどうかを判断します特定のURIのPUTメソッドをサポートします。サポートしている場合は、ユーザーがドキュメントへの変更をリモートサーバーに直接保存できるようにドキュメントを開きます。この機能は、これらの特定のMicrosoftアプリケーション内に緊密に統合されています。

GET

これは、毎日の使用で断然最も一般的な方法です。Webブラウザに通常のドキュメントをロードするたびに、それはGETリクエストになります。

GETメソッドは、サーバーが特定のドキュメントを返すように要求します。サーバーに送信する必要がある唯一のデータは、サーバーがどのドキュメントを返すかを決定するために必要な情報です。これには、サーバーがドキュメントを動的に生成するために使用できる情報を含めることができます。この情報は、リクエストURIのヘッダーやクエリ文字列の形式で送信されます。この件については、Cookieがリクエストヘッダーで送信されます。

HEAD

このメソッドはGETメソッドと同じですが、1つの違いがあります。サーバーは、応答に含まれるヘッダーのみを返す場合、要求されたドキュメントを返しません。これは、たとえば、ドキュメント全体を転送して処理することなく、特定のドキュメントが存在するかどうかを判断するのに役立ちます。

POST

これは2番目に一般的に使用される方法であり、おそらく最も複雑です。POSTメソッドリクエストは、サーバー上で状態を変更する可能性のあるいくつかのアクションを呼び出すためにほぼ排他的に使用されます。

POSTリクエストは、GETやHEADとは異なり、リクエストメッセージの本文に一部のデータを含めることができます(通常はそうします)。このデータは任意の形式にすることができますが、最も一般的には、クエリ文字列(要求URIに表示されるのと同じ形式)またはファイルの添付ファイルとともにキーと値のペアを通信できるマルチパートメッセージです。

多くのHTMLフォームはPOSTメソッドを使用します。ブラウザからファイルをアップロードするには、フォームにPOSTメソッドを使用する必要があります。

POSTメソッドはべき等ではないため、RESTfulAPIと意味的に互換性がありません。つまり、2番目の同一のPOSTリクエストにより、サーバーの状態がさらに変化する可能性があります。これは、RESTの「ステートレス」制約と矛盾します。

PUT

これはGETを直接補完します。GET要求が、サーバーが応答本文の要求URIで指定された場所にドキュメントを返す必要があることを示している場合、PUTメソッドは、サーバーが要求URIで指定された場所の要求本文にデータを格納する必要があることを示します。

DELETE

これは、サーバーがリクエストURIで示された場所でドキュメントを破棄する必要があることを示しています。かなり明白な理由で、インターネットに面したHTTPサーバーの実装は、DELETE要求を受信したときにアクションを実行することはほとんどありません。

TRACE

これにより、アプリケーション層レベルのメカニズムが提供され、クライアントは、送信先サーバーに到達するまでに送信された要求を確認できるようになります。これは、クライアントと宛先サーバーの間のプロキシサーバーが要求メッセージに与える影響を判断するのに最も役立ちます。

CONNECT

HTTP 1.1は、CONNECTメソッドの名前を予約しますが、その使用法や目的さえも定義しません。その後、一部のプロキシサーバーの実装では、CONNECTメソッドを使用してHTTPトンネリングを容易にしています。

于 2012-08-17T13:00:14.777 に答える
6

私はPUTを使ったことがありません(GET POSTとFILESで十分でした)が、この例はphpドキュメントからのものなので、役立つかもしれません(http://php.net/manual/en/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);
?>
于 2012-08-17T12:30:17.133 に答える
3

これが私が最も有用だと思った解決策です。

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

$putで見慣れているのと同じように配列になりますが、$_POST真のRESTHTTPプロトコルに従うことができるようになりました。

于 2016-02-19T04:16:01.010 に答える
1

POSTを使用し、実際のメソッド(この場合はPUT)を示すXヘッダーを含めます。通常、これは、GETとPOST以外のメソッドを許可しないファイアウォールを回避する方法です。PHPのバグを宣言するだけで(マルチパートPUTペイロードの処理を拒否するため、バグがあります)、古いファイアウォールや厳格なファイアウォールと同じように扱います。

GETに関してPUTが何を意味するかについての意見は、まさにその意見です。HTTPではそのような要件はありません。それは単に「同等」と述べています..「同等」が何を意味するかを決定するのは設計者次第です。デザインが複数ファイルのアップロードPUTを受け入れ、同じリソースの後続のGETに対して「同等の」表現を生成できる場合、それは技術的にも哲学的にも、HTTP仕様で問題なくダンディです。

于 2014-10-18T16:28:35.233 に答える
0

DOCに書かれていることに従ってください:

<?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);
?>

これにより、PUTストリームにあるファイル全体読み取られ、ローカルに保存されます。そうすれば、必要な処理を実行できます。

于 2012-08-17T12:30:39.457 に答える