You wouldn't tell the user "please open the FTP client, upload the file to http://www.jb51.net/uploads/, and name it 2dk433423l.jpg" when they want to upload their avatar, right?
Uploading based on HTTP is relatively much easier to use and more secure than FTP. There are three uploading methods that can be applied: PUT, WEBDAV, and RFC1867. This article will analyze in PHP, which are How to implement file upload based on RFC1867.
RCF1867 is the Form-based File Upload in HTML standard protocol. The RFC1867 standard makes two modifications to HTML:
1 为input元素的type属性增加了一个file选项。<br>2 input标记可以具有accept属性,该属性能够指定可被上传的文件类型或文件格式列表。<br>
In addition, this standard also defines a new mime type: multipart/form-data, and when processing a file with enctype="multipart/form-data" and/or containing .
For example, when HTML wants the user to upload one or more files, he can write:
<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>
This form must be familiar to everyone, but for PHP, it defines a default form element MAX_FILE_SIZE. Users can use this hidden form element to suggest that PHP only allows the maximum size of the uploaded file, such as the above For example, we hope that the file uploaded by the user cannot be larger than 5000 (5k) bytes, then we can write it as follows:
<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>
Let’s not talk about how unreliable this MAX_FILE_SIZE is (so browser-based control is unreliable). From a purely implementation perspective, I will slowly introduce how this MAX_FILE_SIZE works.
When the user selects a file (laruence.txt), fills in the file description ("Laruence's personal introduction"), and clicks upload, what happens?
After the user confirms submission, the browser will send a data packet in a similar format to the page specified by the action attribute in the form (upload.php in this example):
//请求头<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>
The next step is how the server processes the data.
When the web server, assumed here to be Apache (also assume that PHP is installed on Apache as a module), receives the user's data, it first determines the MIME TYPE as the PHP type based on the HTTP request header, and then After some processes (for this part, please refer to my previous PHP Life Cycle ppt), control will eventually be handed over to the PHP module.
At this time, PHP will call sapi_activate to initialize a request. In this process, first determine the request type, which is POST at this time, and then call sapi_read_post_data. Through Content-type, find the rfc1867 processing function rfc1867_post_handler, and then call this handler, to analyze the data from POST.
The source code for rfc1867_post_handler can be found in mian/rfc1867.c. You can also refer to my previous in-depth understanding of PHP file upload, which also lists the source code.
Then, PHP passes the boundary, and for each segment, checks whether it is also defined:
name和filename属性(有名文件上传)<br> 没有定义name定义了filename(无名上传)<br> 定义了name没有定义filename(普通数据),<br>
Thus performing different processing.
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>
During this process, PHP will check whether there is MAX_FILE_SIZE in the ordinary data.
/* 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 yes, it will check whether the file size is exceeded according to its value.
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>
Through the above code, we can also see that the judgment is divided into two parts. The first part is to check the default upload limit of PHP. The second part is to check the user-defined MAX_FILE_SIZE, so the MAX_FILE_SIZE defined in the form cannot Exceeded the maximum upload file size set in PHP.
By judging the name and filename, if it is a file upload, a temporary file with a random name will be created in the file upload directory according to the settings of 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>
Returns the file handle, and a temporary random file name.
After that, there will be some verification, such as the file name is legal, the name is legal, etc.
If these verifications pass, then read the content and write it to this temporary file.
.....<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>
When the loop reading is completed, close the temporary file handle. Record the temporary variable name:
zend_hash_add(SG(rfc1867_uploaded_files), temp_filename,<br> strlen(temp_filename) + 1, &temp_filename, sizeof(char *), NULL);<br>
and generate the FILE variable. At this time, if it is a famous upload, it will be set:
$_FILES['userfile'] //name="userfile"<br>
If it is an unnamed upload, tmp_name will be used to set it:
$_FILES['tmp_name'] //无名上传<br>
Finally handed over to upload.php written by the user.
At this time in upload.php, the user can operate the file just generated through move_uploaded_file~