A free and open-source book on ZF3 for beginners

10.1. About HTTP File Uploads

HTML forms have capability for uploading files of arbitrarily large size 39. The files are typically transmitted through HTTP POST method 40.

39) HTTP file uploads are described in RFC-1867. This mechanism allows to upload large files by using binary content transfer encoding. The "multipart/form-data" encoding type is utilized for this purpose.

40) The HTTP GET method is inefficient for file uploads, because URL length has some upper limit. Also, URL-encoding the file data greatly increases the URL length.

By default, HTTP uses the URL encoding for transfers of form data, and you could see how that encoding looks like in previous chapters. However, this encoding is inefficient for uploading large files, since URL-encoding binary data dramatically increases the length of the HTTP request. For the purpose of uploading files, it is instead recommended to use the so called "binary transfer encoding" described in the next section.

10.1.1. HTTP Binary Transfer Encoding

A simple HTML form capable of file uploads is shown in the code example below. The binary encoding type is enabled by setting the enctype attribute of the form with the value of "multipart/form-data":

<form action="upload" method="POST" enctype="multipart/form-data">
    <input type="file" name="myfile">
    <input type="submit" name="Submit">

In line 1, we explicitly set form encoding (enctype attribute) to "multipart/form-data" to utilize effective binary content transfer encoding for the form.

In line 2, we define an input field with type "file" and name "myfile". This input field will allow site visitor to select the file for upload.

If you now save the above mentioned markup to an .html file and open it in your web browser, you will see the page like in figure 10.1.

Figure 10.1. A simple HTML form capable of file upload Figure 10.1. A simple HTML form capable of file upload

The file element has the Browse... button allowing to pick a file for upload. When the site user picks some file and clicks the Submit button on the form, the web browser will send an HTTP request to the web server, and the request will contain the data of the file being uploaded. The example below illustrates how the HTTP request may look like:

POST http://localhost/upload HTTP/1.1
Host: localhost
Content-Length: 488
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) 
Content-Type: multipart/form-data; boundary=----j1bOrwgLvOC3dy7o
Accept-Encoding: gzip,deflate,sdch

Content-Disposition: form-data; name="myfile"; filename="Somefile.txt"
Content-Type: text/html

(file binary data goes here)
Content-Disposition: form-data; name="Submit"

Submit Request

As you can see from the example above, the HTTP request with "multipart/form-data" encoding type looks analogous to a usual HTTP request (has the status line, the headers, and the content area), however it has the following important differences:

By default, PHP engine's settings do not allow to upload large files (larger than 2MB). In order to upload large files, you may need to edit the php.ini configuration file and modify the post_max_size and upload_max_filesize parameters (please refer to Appendix A. Configuring Web Development Environment for information on how to do that). Setting these with 100M allows to upload files up to 100 Mb in size, and this would typically be sufficient. If you plan to upload very large files up to 1 GB in size, than better set these with 1024M. Do not forget to restart your Apache Web Server after editing the configuration file.

10.1.2. $_FILES Super-Global Array in PHP

When a site visitor uploads some files to your Apache Web Server, the files are placed to a temporary location (usually to system temporary directory that is /tmp in Linux and C:\Windows\Temp in Windows). The PHP script receives the file information to the special super-global array named $_FILES.

The $_FILES array is analogous to the $_GET and $_POST super-globals. The latter two are used to store the GET and POST variables, respectively, while the first one is used to store information about uploaded files.

For example, for the above mentioned simple upload form, the $_FILES super-global array will look as follows (the output is generated with the var_dump() PHP function):

array (size=1)
    'myfile' => 
        array (size=5)
            'name' => string 'somefile.txt' (length=12)
            'type' => string 'text/plain' (length=10)
            'tmp_name' => string '/tmp/phpDC66.tmp' (length=16)
            'error' => int 0
            'size' => int 18

As you can see from the example above, the $_FILES array contains an entry per each uploaded file. For each uploaded file, it contains the following information:

41) MIME type, also known as "content type" is a standard identifier used on the Internet to indicate the type of data that a file contains. For example the "text/plain" MIME type is assigned to a text file, while the "application/octet-stream" MIME type is assigned to a binary file.

PHP engine stores the uploaded files in a temporary location which is cleaned up as soon as the PHP script execution ends. So, if you want to save the uploaded files to some directory for later use, you need to utilize the move_uploaded_file() PHP function. The move_uploaded_file() function takes two arguments: the first one is the name of the temporary file, and the second one is the destination file name.

You might be confused why you cannot use the usual rename() PHP function for moving the temporary uploaded file to its destination path. PHP has special function for moving uploaded files for security reasons. The move_uploaded_file() function is analogous to rename() function, but it takes some additional checks to ensure the file was really transferred through HTTP POST request, and that the upload process has finished without errors.

The following code example shows how to move the file uploaded with the simple form we have considered above:

$destPath = '/path/to/your/upload/dir';
$result = move_uploaded_file($_FILES['myfile']['tmp_name'], $destPath);
if(!$result) {
    // Some error occurred.

Above, in line 1, we set the $destPath with the directory name where to save the uploaded file.

In line 2, we call the move_uploaded_file() function and pass it two arguments: the path to the temporary file and the destination path.

Specifying the directory name as the second argument of the move_uploaded_file() function is suitable when you do not want to rename the file. If you need to save the uploaded file under another name than its original name, you can specify the full file path instead of the directory name.

In line 3, we check the returned value of the function. If the operation is successful, the function will return true. If some error occurs (for example, if directory permissions are insufficient to save the file), the boolean false will be returned.