web-dev-qa-db-ja.com

file_get_contentsを使用してファイルをアップロードする

CURLを使用してこれを非常に簡単に実行できることはわかっていますが、httpストリームコンテキストでfile_get_contents()を使用してリモートWebサーバーにファイルをアップロードすることが可能かどうか疑問に思っていました。

37
Shabbyrobe

まず最初に、multipart Content-Typeの最初のルールは境界を定義です。これは、各パーツ間の区切り文字として使用されます(名前が示すように、複数の部品)。境界はコンテンツ本文に含まれていない任意の文字列にすることができます。私は通常タイムスタンプを使用します:

define('MULTIPART_BOUNDARY', '--------------------------'.microtime(true));

境界を定義したら、Content-Typeヘッダーを付けて境界を送信し、期待する区切り文字をWebサーバーに通知する必要があります。

$header = 'Content-Type: multipart/form-data; boundary='.MULTIPART_BOUNDARY;

それが完了したら、HTTP仕様と送信したヘッダーに一致する適切なコンテンツ本文を作成する必要があります。ご存知のように、フォームからファイルをPOSTするときは、通常、フォームフィールド名があります。それを定義します:

// equivalent to <input type="file" name="uploaded_file"/>
define('FORM_FIELD', 'uploaded_file'); 

次に、コンテンツ本文を作成します。

$filename = "/path/to/uploaded/file.Zip";
$file_contents = file_get_contents($filename);    

$content =  "--".MULTIPART_BOUNDARY."\r\n".
            "Content-Disposition: form-data; name=\"".FORM_FIELD."\"; filename=\"".basename($filename)."\"\r\n".
            "Content-Type: application/Zip\r\n\r\n".
            $file_contents."\r\n";

// add some POST fields to the request too: $_POST['foo'] = 'bar'
$content .= "--".MULTIPART_BOUNDARY."\r\n".
            "Content-Disposition: form-data; name=\"foo\"\r\n\r\n".
            "bar\r\n";

// signal end of request (note the trailing "--")
$content .= "--".MULTIPART_BOUNDARY."--\r\n";

ご覧のとおり、Content-Dispositionヘッダーとform-dataディスポジションをnameパラメータ(フォームフィールド名)とfilenameパラメータとともに送信しています(元のファイル名)。 Content-Typeを正しく入力する場合は、$_FILES[]['type']ヘッダーを適切なMIMEタイプで送信することも重要です。

アップロードするファイルが複数ある場合は、$ contentビットを使用してプロセスを繰り返すだけで、もちろん、ファイルごとに異なるFORM_FIELDを使用します。

次に、コンテキストを作成します。

$context = stream_context_create(array(
    'http' => array(
          'method' => 'POST',
          'header' => $header,
          'content' => $content,
    )
));

そして実行:

file_get_contents('http://url/to/upload/handler', false, $context);

注:バイナリファイルを送信する前にエンコードする必要はありません。 HTTPはバイナリを問題なく処理できます。

84
netcoder