PHP file upload source code analysis (RFC1867) if you need to know, please refer
HTTP-based upload is easier to use and more secure than FTP. you can use PUT, WEBDAV, and RFC1867 upload methods. This article will analyze how to implement File Upload Based on RFC1867 in PHP.
RFC1867
RCF1867 is the Form-based File Upload in HTML standard protocol. RFC1867 makes two changes to HTML:
1 adds a file option for the type attribute of the input element.
2 The input mark can have the accept attribute, which can specify the file type or file format list that can be uploaded.
In addition, this standard defines a new mime type: multipart/form-data, and the action that should be taken when processing a form with enctype = "multipart/form-data" and/or a flag containing <input type = "file">.
For example, when HTML allows users to upload one or more files, they can write the following code:
The Code is as follows: |
Copy code |
<Form enctype = "multipart/form-data" action = "upload. php" method = post> Select File: <Input name = "userfile" type = "file"> File description: <Input name = "description" type = "text"> <Input type = "submit" value = "Upload"> </Form> |
This form must be familiar to everyone. For PHP, it also defines a default form Element MAX_FILE_SIZE, you can use this hidden form element to recommend that PHP only allow the size of the file to be uploaded. For example, we hope that the size of the File Uploaded by the user cannot exceed 5000 (5 k) bytes, you can write as follows:
The Code is as follows: |
Copy code |
<Form enctype = "multipart/form-data" action = "upload. php" method = post> <Input type = "hidden" value = "5000" name = "MAX_FILE_SIZE"> <! -- File size --> Select File: <Input name = "userfile" type = "file"> File description: <Input name = "description" type = "text"> <Input type = "submit" value = "Upload"> </Form> |
Not to mention, how unreliable MAX_FILE_SIZE is (So browser-based control is unreliable). Simply by implementation, I will slowly introduce how MAX_FILE_SIZE works.
When the user selects a file (laruence.txt) and fills in the file description ("laruence's personal introduction"), what happened after clicking upload?
Form submission
After the user confirms the submission, the browser will send data packets in similar formats to the page specified by the action attribute in form (in this example, upload. php ):
The Code is as follows: |
Copy code |
// Request Header POST/upload. php HTTP/1.0rn ... Host: www. laruence. comrn ... Content-length: xxxxxrn ... Content-type: multipart/form-data, boundary = -------------- 7d51863950254rn ... Rnrn // Start POST data content --------------- 7d51863950254 Content-disposition: form-data; name = "description" Laruence Profile --------------- 7d51863950254 Content-disposition: form-data; name = "userfile"; filename = "laruence.txt" Content-Type: text/plain ... Content of laruence.txt... --------------- 7d51863950254 |
The next step is how the server processes the data.
Upload accepted
When the Web server is Apache (and PHP is installed on Apache in module mode) and user data is received, it first follows the HTTP request header, after determining that the mime type is php type and going through some processes (for this part, refer to my previous PHP Life Cycle ppt), the control will 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, and then call sapi_read_post_data through Content-type, find the rfc1867 handler rfc1867_post_handler and call this handler to analyze the POST data.
For the source code of rfc1867_post_handler, you can find it in mian/rfc1867.c. For more information, see my previous in-depth understanding of PHP file upload, which also lists the source code.
Then, PHP uses boundary to check whether each segment is defined simultaneously:
Name and filename attributes (famous File Upload)
Filename is not defined)
Filename (common data) is not defined for name ),
To perform different processing.
The Code is as follows: |
Copy code |
If (cd = php_mime_get_hdr_value (header, "Content-Disposition "))){ Char * pair = NULL; Int end = 0; While (isspace (* cd )){ ++ Cd; } While (* cd & (pair = php_ap_getword (& cd ,';'))) { Char * key = NULL, * word = pair; While (isspace (* cd )){ ++ Cd; } If (strchr (pair, '= ')){ Key = php_ap_getword (& pair, '= '); If (! Strcasecmp (key, "name ")){ // Obtain the name field If (param ){ Efree (param ); } Param = php_ap_getword_conf (& pair TSRMLS_CC ); } Else if (! Strcasecmp (key, "filename ")){ // Obtain the filename field If (filename ){ Efree (filename ); } Filename = php_ap_getword_conf (& pair TSRMLS_CC ); } } If (key ){ Efree (key ); } Efree (word ); } |
In this process, PHP will check whether MAX_FILE_SIZE exists in common data.
The Code is as follows: |
Copy code |
/* Normal form variable, safe to read all data into memory */ If (! Filename & param ){ Unsigned int value_len; Char * value = multipart_buffer_read_body (mbuff, & value_len TSRMLS_CC ); Unsigned int new_val_len;/* Dummy variable */ ...... If (! Strcasecmp (param, "MAX_FILE_SIZE ")){ Max_file_size = atol (value ); } Efree (param ); Efree (value ); Continue; } |
If yes, the system checks whether the file size exceeds the value.
The Code is as follows: |
Copy code |
If (PG (upload_max_filesize)> 0 & total_bytes> PG (upload_max_filesize )){ Cancel_upload = UPLOAD_ERROR_A; } Else if (max_file_size & (total_bytes> max_file_size )){ # If DEBUG_FILE_UPLOAD Sapi_module.sapi_error (E_NOTICE, "MAX_FILE_SIZE of % ld bytes exceeded-file [% s = % s] not saved ", Max_file_size, param, filename ); # Endif Cancel_upload = UPLOAD_ERROR_ B; } |
Through the above code, we can also see that the judgment is divided into two parts, the first part is to check the default PHP Upload ceiling. the second part is to check the User-Defined MAX_FILE_SIZE. Therefore, the MAX_FILE_SIZE defined in the form cannot exceed the maximum size set in PHP.
By judging the name and filename, if a file is uploaded, a temporary file with a random name will be created in the file upload directory based on php settings:
The Code is as follows: |
Copy code |
If (! Skip_upload ){ /* Handle file */ Fd = php_open_temporary_fd_ex (PG (upload_tmp_dir ), "Php", & temp_filename, 1 TSRMLS_CC ); If (fd =-1 ){ Sapi_module.sapi_error (E_WARNING, "File upload error-unable to create a temporary file "); Cancel_upload = UPLOAD_ERROR_E; } } |
Returns the file handle and temporary random file name.
There will also be some verification, such as the file name is legal, name is legal, and so on.
If these verifications are passed, read the content and write it to the temporary file.
.....
The Code is as follows: |
Copy code |
Else if (blen> 0 ){ Wlen = write (fd, buff, blen); // write a temporary file. If (wlen =-1 ){ /* Write failed */ # If DEBUG_FILE_UPLOAD Sapi_module.sapi_error (E_NOTICE, "write () failed-% s", strerror (errno )); # Endif Cancel_upload = UPLOAD_ERROR_F; } } .... |
When the cyclic reading is complete, close the temporary file handle. Record the temporary variable name:
The Code is as follows: |
Copy code |
Zend_hash_add (SG (rfc1867_uploaded_files), temp_filename, Strlen (temp_filename) + 1, & temp_filename, sizeof (char *), NULL ); |
And generate the FILE variable. At this time, if it is a famous upload, It will be set:
The Code is as follows: |
Copy code |
$ _ FILES ['userfile'] // name = "userfile" |
If it is an anonymous upload, tmp_name will be used for setting:
The Code is as follows: |
Copy code |
$ _ FILES ['tmp _ name'] // upload without name |
It is finally handed over to the user for upload. php processing.
In upload. php, you can use move_uploaded_file to operate the generated file.