<input type="file" /> で指定したファイルは、リクエストのボディ部に MIME の multipart という形式で格納されてサーバに送信されます。
http://www.atmarkit.co.jp/ait/articles/0104/18/news002.html
# 上記はメールの添付ファイルについて書かれていますが、<input type="file" /> で送信するときも同じです。
バイナリのファイルは BASE64 という形式でテキスト化されます。
BASE64 でエンコードされたデータは、大体 4割くらい元のデータよりもサイズが大きくなります。
http://ja.wikipedia.org/wiki/Base64
PHP の最新は 5.6.6 でしょうか。
ダウンロードページから、ソースを落として読んでみました。
http://php.net/downloads.php
BASE64 のデコード、つまりリクエストで送られた MIME の multipart 形式で格納されたデータを境界で切り分けて、ここのファイルをバイナリに戻す時に呼び出される関数(のはず)です。
php-5.6.6/ext/standard/base64.c
/* {{{ proto string base64_decode(string str[, bool strict]) Decodes string using MIME base64 algorithm */ PHP_FUNCTION(base64_decode) { char *str; unsigned char *result; zend_bool strict = 0; int str_len, ret_length; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &str, &str_len, &strict) == FAILURE) { return; } result = php_base64_decode_ex((unsigned char*)str, str_len, &ret_length, strict); if (result != NULL) { RETVAL_STRINGL((char*)result, ret_length, 0); } else { RETURN_FALSE; } } /* }}} */
パラメータ領域からデータを切り出して、str が指す領域に格納し、php_base64_decode_ex という関数でデコード(バイナリに戻す)したデータのアドレスを戻します。
少なくとも、リクエストパラメータはメモリ領域にすべて展開されること、BASE64 でエンコードされたパラメータが保持されたままで、ひとつのファイルはデコードされる、ということが分かります。
つまり、1Mbyte のファイルを 5つ送った場合には、
の、計 8Mbyte は 少なくともメモリ上に確保されます。
# MIME multipart の境界やヘッダ、その他のリクエストパラメータは、誤差の範囲
アップロードされたファイルの実体は $_FILES['userfile']['tmp_name'] でアクセスされるということは、一旦、テンポラリの領域に実データとして保存されているということなので、送信した全てのファイルがデコードされた状態でメモリに展開されるわけではなく、ひとつのファイルをデコードしてテンポラリ領域に保存する、というふうに処理されていると思います。
共有のレンタルサーバーであれば、一度にアップロード出来る容量は、サーバ側で2?5Mbyte程度に制限されています。
また、1つのPHPが使用できるメモリの容量も128Mbyteになっているのが通常です。
実行時間も通常30秒以内に限られています。
PHP を触り始めて 1 年にも満たないのでそれほど詳しいわけではないですが、最近同じことを調べたので参考になればと思います。
テンポラリファイルが作成され終わるまでの間の処理で、アップロードされたファイルがそのままメモリに展開されることはないようです。
参考 URL
http://stackoverflow.com/questions/14714226/large-php-file-upload
ただし、テンポラリファイルが作成された後、PHP のコードでファイルを移動したりする際には、そのメソッドがメモリをどのように消費するのか、max_execution_time の対象となる処理なのかを気にする必要があります。
巨大なファイルをアップロードする場合に影響する php.ini などの設定は以下の項目だと思っています。
PHP の最大実行時間を制限するものです。
この項目を 5 秒に設定し、数百 KB のファイルをアップロードした場合は
問題ありませんでしたが、4 GB 程度のファイルをアップロードした場合は
エラーが発生しました。
60 秒に設定した場合、どちらの場合でも問題ありませんでした。
4 GB のファイルアップロードに用した時間は 120 秒程度です。
以上のことから、アップロード中の通信待ちの時間はこの対象には
含まれないが、ファイルの移動処理の一部の時間は
対象になっているのかもしれません。それ以上詳しくは調べませんでした。
POST データに許可される最大サイズを設定します。
これは単純にサイズを制限するので、想定される最も大きいサイズを指定すれば
良いと思います。
PHP や Apache などが 32 bit 版だと整数の最大値を気にしなければならない
かもしれません。
ちなみに、私の場合 Windows 7、PHP、Apache のいずれも 64 bit 版の環境で
4G と指定したらエラーが出ました。原因はわかりませんが、
4294967296 のように記述するとエラーがでなくなりました。
アップロードできる 1 ファイルあたりの最大サイズです。
アップロードされるファイルのデータは POST データに含まれるので、
post_max_size で指定した値よりも小さくなければなりません。
この設定も post_max_size と同様に整数の制限があります。
// index.php
?>
<html>
<head>
// upload.php
// http://qiita.com/mpyw/items/939964377766a54d4682
header('Content-Type: text/html; charset=utf-8');
header('X-Content-Type-Options: nosniff');
try {
// 未定義である・複数ファイルである・$_FILES Corruption 攻撃を受けた
// どれかに該当していれば不正なパラメータとして処理する
if (
!isset($_FILES['user_upload_file']['error']) ||
!is_int($_FILES['user_upload_file']['error'])
) {
throw new RuntimeException('パラメータが不正です');
}
// $_FILES['upfile']['error'] の値を確認
switch ($_FILES['user_upload_file']['error']) {
case UPLOAD_ERR_OK: // OK
break;
case UPLOAD_ERR_NO_FILE: // ファイル未選択
throw new RuntimeException('ファイルが選択されていません');
case UPLOAD_ERR_INI_SIZE: // php.ini定義の最大サイズ超過
case UPLOAD_ERR_FORM_SIZE: // フォーム定義の最大サイズ超過
throw new RuntimeException('ファイルサイズが大きすぎます');
default:
throw new RuntimeException('その他のエラーが発生しました');
}
// $_FILES['upfile']['mime']の値はブラウザ側で偽装可能なので
// MIMEタイプに対応する拡張子を自前で取得する
$finfo = new finfo(FILEINFO_MIME_TYPE);
if (!$ext = array_search(
$finfo->file($_FILES['user_upload_file']['tmp_name']),
array(
'gif' => 'image/gif',
'jpg' => 'image/jpeg',
'png' => 'image/png',
),
true
)) {
throw new RuntimeException('ファイル形式が不正です');
}
// ファイルデータからSHA-1ハッシュを取ってファイル名を決定し、保存する
if (!move_uploaded_file(
$_FILES['user_upload_file']['tmp_name'],
$path = sprintf('../../upload-test/%s.%s',
sha1_file($_FILES['user_upload_file']['tmp_name']),
$ext
)
)) {
throw new RuntimeException('ファイル保存時にエラーが発生しました');
}
// ファイルのパーミッションを確実に0644に設定する
chmod($path, 0644);
echo 'ファイルは正常にアップロードされました';
} catch (RuntimeException $e) {
echo $e->getMessage();
}
?>
アップロードページに戻る