ユーザーが自分のアバターをアップロードしたいときに、「FTP クライアントを開いて、ファイルを http://www.jb51.net/uploads/ にアップロードし、名前を 2dk433423l.jpg にしてください」とは言いませんよね。
HTTP に基づくアップロードは、FTP よりもはるかに使いやすく安全です。適用できるアップロード方法は、PUT、WEBDAV、および RFC1867 です。この記事では、ファイルのアップロードを実装する方法について説明します。 RFC1867に基づく
。RCF1867 は、HTML 標準プロトコルのフォームベースのファイル アップロードです。RFC1867 標準では、
1 为input元素的type属性增加了一个file选项。<br>2 input标记可以具有accept属性,该属性能够指定可被上传的文件类型或文件格式列表。<br>
さらに、この規格は新しい 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~
を通じて生成されたばかりのファイルを操作できます。