PHPファイルアップロードのソースコード解析(RFC1867)

WBOY
リリース: 2016-06-13 12:21:10
オリジナル
1094 人が閲覧しました

ユーザーが自分のアバターをアップロードしたいときに、「FTP クライアントを開いて、ファイルを http://www.jb51.net/uploads/ にアップロードし、名前を 2dk433423l.jpg にしてください」とは言いませんよね。

HTTP に基づくアップロードは、FTP よりもはるかに使いやすく安全です。適用できるアップロード方法は、PUT、WEBDAV、および RFC1867 です。この記事では、ファイルのアップロードを実装する方法について説明します。 RFC1867に基づく

RFC1867

RCF1867 は、HTML 標準プロトコルのフォームベースのファイル アップロードです。RFC1867 標準では、

1 为input元素的type属性增加了一个file选项。<br>2 input标记可以具有accept属性,该属性能够指定可被上传的文件类型或文件格式列表。<br>
ログイン後にコピー
という 2 つの変更が加えられています。


さらに、この規格は新しい MIME タイプ multipart/form-data も定義しており、enctype="multipart/form-data" を指定したファイルや でマークされているときに取られる必要があります。

たとえば、HTML がユーザーに 1 つ以上のファイルをアップロードしてもらいたい場合、次のように記述できます:

<form enctype="multipart/form-data" action="upload.php" method=post><br>选择文件:<br><input name="userfile" type="file"><br>文件描述:<br><input name="description" type="text"><br><input type="submit" value="上传"><br></form><br>
</p>
<p>このフォームは誰もがよく知っているはずですが、PHP ではデフォルトのフォーム要素 MAX_FILE_SIZE が定義されており、ユーザーはこの非表示のフォーム要素を使用して、PHP がアップロードされるファイルの最大サイズのみを許可することを示唆できます。たとえば、次のようになります。ユーザーがアップロードするファイルが 5000 (5k) バイトを超えないようにする場合は、次のように記述できます: </p>
<pre class="brush:php;toolbar:false"><form enctype="multipart/form-data" action="upload.php" method=post><br><input type="hidden" value="5000" name="MAX_FILE_SIZE"> <!--文件大小--><br>选择文件:<br><input name="userfile" type="file"><br>文件描述:<br><input name="description" type="text"><br><input type="submit" value="上传"><br></form><br>
ログイン後にコピー

この MAX_FILE_SIZE がどれほど信頼できないかについては話さないようにします (したがって、ブラウザベースの制御は信頼できません)。純粋に実装の観点から、この MAX_FILE_SIZE がどのように機能するかをゆっくりと紹介します。

ユーザーがファイル (laruence.txt) を選択し、ファイルの説明 (「Laruence の個人紹介」) を入力してアップロードをクリックすると、何が起こりますか?

フォーム送信

ユーザーが送信を確認した後、ブラウザは、フォーム (この例では、upload.php) の action 属性で指定されたページに、同様の形式のデータ パケットを送信します。

//请求头<br>POST /upload.php HTTP/1.0\r\n<br>...<br>Host: www.laruence.com\r\n<br>...<br>Content-length: xxxxx\r\n<br>...<br>Content-type: multipart/form-data, boundary=--------------7d51863950254\r\n<br>...\r\n\r\n<br>//开始POST数据内容<br>---------------7d51863950254<br>content-disposition: form-data; name="description"<br>laruence的个人介绍<br>---------------7d51863950254<br>content-disposition: form-data; name="userfile"; filename="laruence.txt"<br>Content-Type: text/plain<br>... laruence.txt 的内容...<br>---------------7d51863950254<br>
ログイン後にコピー

次のステップは、サーバーがデータをどのように処理するかです。

アップロードを承認します

ここでは Apache であると仮定します (PHP がモジュールとして Apache にインストールされているとも仮定します) Web サーバーがユーザーのデータを受信すると、まず HTTP リクエスト ヘッダーに基づいて MIME TYPE を PHP タイプとして決定し、いくつかのプロセス (この部分については、以前の PHP ライフ サイクル ppt を参照してください) の後、最終的に制御が PHP モジュールに渡されます。

このとき、PHP は sapi_activate を呼び出してリクエストを初期化します。このプロセスでは、まずリクエストのタイプ (この時点では POST) を決定し、次に、Content-type を通じて rfc1867 処理関数 rfc1867_post_handler を見つけます。次に、このハンドラーを呼び出して、POST からのデータを分析します。

rfc1867_post_handler のソース コードは、mian/rfc1867.c にあります。また、ソース コードのリストも記載されている、PHP ファイルのアップロードに関する以前の詳細な理解を参照することもできます。

次に、PHP は境界を通過し、セグメントごとに、その境界も定義されているかどうかを確認します。

 name和filename属性(有名文件上传)<br> 没有定义name定义了filename(无名上传)<br> 定义了name没有定义filename(普通数据),<br>
ログイン後にコピー

このように、異なる処理が実行されます。

if ((cd = php_mime_get_hdr_value(header, "Content-Disposition"))) {<br> char *pair=NULL;<br> int end=0;<br><br> while (isspace(*cd)) {<br> ++cd;<br> }<br><br> while (*cd && (pair = php_ap_getword(&cd, ';')))<br> {<br> char *key=NULL, *word = pair;<br><br> while (isspace(*cd)) {<br> ++cd;<br> }<br><br> if (strchr(pair, '=')) {<br> key = php_ap_getword(&pair, '=');<br><br> if (!strcasecmp(key, "name")) {<br> //获取name字段<br> if (param) {<br> efree(param);<br> }<br> param = php_ap_getword_conf(&pair TSRMLS_CC);<br> } else if (!strcasecmp(key, "filename")) {<br> //获取filename字段<br> if (filename) {<br> efree(filename);<br> }<br> filename = php_ap_getword_conf(&pair TSRMLS_CC);<br> }<br> }<br> if (key) {<br> efree(key);<br> }<br> efree(word);<br> }<br>
ログイン後にコピー

このプロセス中に、PHP は通常のデータに MAX_FILE_SIZE があるかどうかを確認します。

 /* Normal form variable, safe to read all data into memory */<br>if (!filename && param) {<br> unsigned int value_len;<br> char *value = multipart_buffer_read_body(mbuff, &value_len TSRMLS_CC);<br> unsigned int new_val_len; /* Dummy variable */<br> ......<br><br> if (!strcasecmp(param, "MAX_FILE_SIZE")) {<br> max_file_size = atol(value);<br> }<br><br> efree(param);<br> efree(value);<br> continue;<br>}<br>
ログイン後にコピー

「はい」の場合、ファイル サイズがその値に従って超過しているかどうかがチェックされます。

if (PG(upload_max_filesize) > 0 && total_bytes > PG(upload_max_filesize)) {<br> cancel_upload = UPLOAD_ERROR_A;<br>} else if (max_file_size && (total_bytes > max_file_size)) {<br>#if DEBUG_FILE_UPLOAD<br> sapi_module.sapi_error(E_NOTICE,<br> "MAX_FILE_SIZE of %ld bytes exceeded - file [%s=%s] not saved",<br> max_file_size, param, filename);<br>#endif<br> cancel_upload = UPLOAD_ERROR_B;<br>}<br>
ログイン後にコピー

上記のコードから、判断が 2 つの部分に分かれていることもわかります。最初の部分は PHP のデフォルトのアップロード制限を確認することであり、2 番目の部分はユーザー定義の MAX_FILE_SIZE を確認することです。フォーム内のファイルは、PHP で設定された最大アップロード ファイル サイズを超えることはできません。

名前とファイル名を判断し、ファイルアップロードの場合は、PHP の設定に従ってファイルアップロードディレクトリにランダムな名前の一時ファイルが作成されます:

 if (!skip_upload) {<br> /* Handle file */<br> fd = php_open_temporary_fd_ex(PG(upload_tmp_dir),<br> "php", &temp_filename, 1 TSRMLS_CC);<br> if (fd==-1) {<br> sapi_module.sapi_error(E_WARNING,<br> "File upload error - unable to create a temporary file");<br> cancel_upload = UPLOAD_ERROR_E;<br> }<br>}<br>
ログイン後にコピー

ファイルハンドルと一時的なランダムなファイル名を返します。

その後、ファイル名が正当であるか、名前が正当であるかなど、いくつかの検証が行われます。

これらの検証に合格した場合は、コンテンツを読み取り、この一時ファイルに書き込みます。

.....<br>else if (blen > 0) {<br> wlen = write(fd, buff, blen); //写入临时文件.<br> if (wlen == -1) {<br> /* write failed */<br>#if DEBUG_FILE_UPLOAD<br> sapi_module.sapi_error(E_NOTICE, "write() failed - %s", strerror(errno));<br>#endif<br> cancel_upload = UPLOAD_ERROR_F;<br> }<br>}<br>....<br>
ログイン後にコピー

ループの読み取りが完了したら、一時ファイル ハンドルを閉じ、一時変数名を記録します:

zend_hash_add(SG(rfc1867_uploaded_files), temp_filename,<br> strlen(temp_filename) + 1, &temp_filename, sizeof(char *), NULL);<br>
ログイン後にコピー

と FILE 変数を生成します。このとき、有名なアップロードの場合は、

$_FILES['userfile'] //name="userfile"<br>
ログイン後にコピー
が設定されます。

名前のないアップロードの場合、tmp_name を使用して設定されます:

$_FILES['tmp_name'] //无名上传<br>
ログイン後にコピー

最終的に、ユーザーが作成した Upload.php に処理用に渡されます。

この時点で、upload.php では、ユーザーは move_uploaded_file~

を通じて生成されたばかりのファイルを操作できます。
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のおすすめ
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート