Php file upload source code analysis (RFC1867 ). Name the secret? HTTP-based users do not always tell users when they want to upload their portraits. "Please open the FTP client, upload the file to http://www.jb51.net/uploads/, and name it 2dk433423l.jpg?
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 when processing a file with enctype = "multipart/form-data" and/or containsActions to be taken when marking the form.
For example, when HTML allows users to upload one or more files, they can write the following code:
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:
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 ):
// Request header
POST/upload. php HTTP/1.0 \ r \ n
...
Host: www.laruence.com \ r \ n
...
Content-length: xxxxx \ r \ n
...
Content-type: multipart/form-data, boundary = -------------- 7d51863950254 \ r \ n
... \ R \ n
// 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.
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.
/* 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.
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:
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.
.....
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:
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:
$_FILES['userfile'] //name="userfile"
If it is an anonymous upload, tmp_name will be used for setting:
$ _ 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 ~
And named 2dk433423l.jpg? HTTP-based...