Discover how to securely and effectively upload files using PHP and MySQL, along with understanding fundamental concerns tied to file uploads such as computer security and legal issues.
This exercise is excerpted from Noble Desktop’s past app development training materials and is compatible with iOS updates through 2021. To learn current skills in web development, check out our coding bootcamps in NYC and live online.
Topics covered in this PHP & MySQL tutorial:
Making a file upload form, The $_FILES array, Uploading files, Basic security
Exercise Overview
Uploading files in PHP is a fairly easy process; however, it is also a dangerous one. You should think carefully before allowing your users to upload files, and you should be just as careful about who you let upload files to your server as you would about letting someone write files to your personal computer.
In this exercise we will show you some very basic security measures as well as how to upload files. It goes without saying that if you put this type of capability on your live site, it should be in a password-protected area or authenticated in some way. You should also be sure your server has some sort of virus protection.
In addition to basic computer security issues, there are other legal issues you should consider—for example, if you are allowing users to upload images to your site. Do the users own the content? Is it legal?
With these considerations in mind, let’s upload some files.
Making a File Upload Form
-
In your code editor, open upload.php from the phpclass folder.
Let’s add a simple form. We’ll set the action to post to itself. Because we are going to upload files, we must also give it the enctype of
"
multipart/form-data"
. -
In between the body tags, add the following bold code:
<form method="post" action="upload.php" enctype="multipart/form-data"> </form>
-
Next we need an input with the type of file. Add the following bold code:
<form method="post" action="upload.php" enctype="multipart/form-data"> <p> <label for="myFile">Please choose a jpg, gif, or png.</label> <input type="file" name="myFile" id="myFile"> </p> </form>
type="file"
tells the browser to allow the user to choose a file for this input. -
Finally, let’s add a submit button. Add the following bold code:
<form method="post" action="upload.php" enctype="multipart/form-data"> <p> <label for="myFile">Please choose a jpg, gif, or png.</label> <input type="file" name="myFile" id="myFile"> </p> <p> <input type="submit" name="upload" id="upload" value="Upload"> </p> </form>
The $_FILES Array
We now have a very basic form with one input. Unlike other form elements such as text inputs, radio buttons, or checkboxes which all get stored in the $_POST
array, any file form elements get put into the $_FILES
array.
Let’s check out this array to see what’s going on.
-
Let’s print out the array if the form has been submitted. At the top of the document, add the following bold code:
<?php if (isset($_POST['upload'])) { print_r($_FILES); } ?>
This will print out the
$_FILES
array if the form has been submitted. -
Save the page and then in a browser go to:
- Mac: localhost:8888/phpclass/upload.php
- Windows: localhost/phpclass/upload.php
-
Click the button to choose a file, then navigate to:
- Mac: Hard Drive > Applications > MAMP > htdocs > phpclass > files
- Windows: C: > xampp > htdocs > phpclass > files
Double–click bird.jpg.
Click Upload.
-
You’ll see the
$_FILES
array printed on screen (formatted nicely for you below), such as:[myFile] => Array ( [name] => bird.jpg [type] => image/jpeg [tmp_name] => /Applications/MAMP/tmp/php/phpcJLXml [error] => 0 [size] => 31553 )
First, everything is stored in an array called myFile. This name comes from the form input we set up earlier.
The myFile array contains a sub-array that has the following elements:
- name: The name of the file.
- type: The type of file.
- tmp_name: The temporary name of the file. When PHP uploads the file, it stores it in a temporary directory. If you don’t do anything with the file or reject the download, PHP will delete the temporary file. This is for security.
- error: An error, if there was any.
- size: The size of the file in bytes.
Some Basic Error Checking
-
In a browser go to:
- Mac: localhost:8888/phpclass/upload.php
- Windows: localhost/phpclass/upload.php
-
This time don’t choose a file, just click Upload.
You’ll see that the
$_FILES
array is still there, but the name value is blank. Next we’ll set our script up to check if the name exists and if not, display a message to the user. Switch back to your code editor.
-
Delete the
print_r
statement, then add the following bold code in its place:<?php if (isset($_POST['upload'])) { if ($_FILES['myFile']['name']) { //upload a file } else { $errorMsg = 'You must choose an image.'; } } ?>
This means: If the file is chosen, upload it; if not set an error message.
-
Let’s display that error message. Right above the form code add the following bold code:
<body> <?php if (isset($errorMsg)) { echo $errorMsg; } ?> <form method="post" action="upload.php" enctype="multipart/form-data">
This will display
$errorMsg
if it is set. -
Save the page and then in a browser go to:
- Mac: localhost:8888/phpclass/upload.php
- Windows: localhost/phpclass/upload.php
Don’t choose a file, just click Upload. You’ll see the error message displayed.
Uploading the File
Let’s actually upload a file. First we must choose a destination. For testing purposes, we will create a folder called upload_test and put it in our phpclass folder. In a live server environment, you can put this wherever is appropriate for your application, or wherever your host will allow. In addition, you may need to set permissions to allow uploads. Contact your host if you are having trouble.
-
First we need to make a folder to upload our files to. Minimize your code editor so you can see the Desktop.
Mac- Navigate to: Hard Drive > Applications > MAMP > htdocs > phpclass
- Make a new folder called: upload_test
- Navigate to: C:/xampp/htdocs/phpclass/
- Make a new folder called: upload_test
Switch back to your code editor.
-
First we’ll set a
$destination
variable. Delete the upload a file comment, then add the following bold code in its place (depending on your platform):if ($_FILES['myFile']['name']) { Mac Users: $destination = '/Applications/MAMP/htdocs/phpclass/upload_test/'; Windows Users: $destination = 'C:xampp/htdocs/phpclass/upload_test/'; } else { $errorMsg = 'You must choose an image.'; }
We’ll use this variable (
$destination
) to tell PHP where to put the file. -
PHP automatically uploads the file, but to actually use it and access it we need to move it to our new directory. We do this with the
move_uploaded_file()
function. This function takes two parameters: the name of the file to move (the temp name), and the location to put the file. For instance:move_uploaded_file(filenameToMove, mydrive/directory/myNewFile.jpg)
-
Underneath the
$destination
variable, add the following bold code:$destination = '/Applications/MAMP/htdocs/phpclass/upload_test/'; move_uploaded_file( $_FILES['myFile']['tmp_name'], $destination .$_FILES['myFile']['name'] );
The first parameter here is the temporary name of the file we just chose. The second is the
$destination
path we set, concatenated with the actual name of the file we chose. -
Save the page and then in a browser go to:
- Mac: localhost:8888/phpclass/upload.php
- Windows: localhost/phpclass/upload.php
-
Click the button to choose a file, then navigate to:
- Mac: Hard Drive > Applications > MAMP > htdocs > phpclass > files
- Windows: C: > xampp > htdocs > phpclass > files
Double–click bird.jpg.
-
Click Upload.
If all goes well, you won’t see any errors.
-
Minimize any windows you may have open so you can see the Desktop.
- Mac: Navigate to Hard Drive > Applications > MAMP > htdocs > phpclass > upload_test.
- Windows: Navigate to C:/xampp/htdocs/phpclass/upload_test.
In the upload_test folder, you’ll see the file you chose!
Some (Very) Basic Security
That’s the basics of uploading a file, but let’s make this a tad more secure. First, we should restrict the file type to only allow GIF, JPEG, and PNG. Second we’ll set the name of the file to a generic one and set the file extension to an appropriate one as well.
The $_FILES['myFile']['type']
variable will allow us to check the type of file the user has chosen. We’ll use a switch statement to check against our three file types and set a file extension. If the file is not one of the proper file types we’ll set the file extension to be blank.
Switch back to your code editor.
-
Above the
$destination
variable, add the following bold code:if ($_FILES['myFile']['name']) { switch($_FILES['myFile']['type']) { case 'image/jpeg': $ext = 'jpg'; break; case 'image/gif': $ext = 'gif'; break; case 'image/png': $ext = 'png'; break; default: $ext = ''; break; } $destination = '/Applications/MAMP/htdocs/phpclass/upload_test/';
This switches through the file type and sets a variable
$ext
which will become our filename extension. -
Let’s wrap our upload code in an if statement so it only runs if the
$ext
variable is NOT blank (that is, is the correct type). Wrap the bold code around the$destination
variable andmove_uploaded_file()
function:if ($ext) { $destination = '/Applications/MAMP/htdocs/phpclass/upload_test/'; move_uploaded_file( $_FILES['myFile']['tmp_name'], $destination .$_FILES['myFile']['name'] ); }
-
While we’re at it, let’s add an else statement that sets an error. Add the following bold code:
if ($ext) { $destination = '/Applications/MAMP/htdocs/phpclass/upload_test/'; move_uploaded_file( $_FILES['myFile']['tmp_name'], $destination .$_FILES['myFile']['name'] ); } else { $errorMsg = 'That file is not the correct type.'; }
If the
$ext
variable is NOT blank, upload the file. Otherwise, set an error message. -
Save the page and then in a browser go to:
- Mac: localhost:8888/phpclass/upload.php
- Windows: localhost/phpclass/upload.php
-
Click the button to choose a file, then navigate to:
- Mac: Hard Drive > Applications > MAMP > htdocs > phpclass > files
- Windows: C: > xampp > htdocs > phpclass > files
Double–click uploadme.txt.
-
Click Upload.
You should see an error message: That file is not the correct type.
Hit the back button in the browser and this time choose a JPG, GIF, or PNG from the folder. The file should upload (check for it in the upload_test folder).
Switch back to your code editor.
-
Finally, let’s give the file we are copying a generic name with a proper extension. We’ll timestamp the name and add an extension. Just above the
$destination
variable, add the following bold code:if ($ext) { $newName = "myImage-"; $newName .= date("Y_n_j-G_i_s."); $newName .= $ext; $destination = '/Applications/MAMP/htdocs/phpclass/upload_test/'; move_uploaded_file( $_FILES['myFile']['tmp_name'], $destination .$_FILES['myFile']['name'] ); }
This sets a new variable,
$newName
, and sets it to be called MyImage-. It then concatenates a date and time, and sets the extension to the variable we set earlier. -
We must now set this as the new name in the
move_uploaded_file()
function. Change themove_uploaded_file()
function as shown in bold:move_uploaded_file( $_FILES['myFile']['tmp_name'], $destination .$newName );
This changes the name from the original filename, to our new filename.
-
Save the page and then in a browser go to:
- Mac: localhost:8888/phpclass/upload.php
- Windows: localhost/phpclass/upload.php
-
Click the button to choose a file, then navigate to:
- Mac: Hard Drive > Applications > MAMP > htdocs > phpclass > files
- Windows: C: > xampp > htdocs > phpclass > files
Double–click bird.jpg.
Click Upload.
Switch to the upload_test folder and see your newly uploaded file. Success!
Switch to your code editor.
Close any open files. We’re done for now.
Bonus (If You Have Time)
The security we have here is decent, but far from foolproof. Depending on the browser, typically MIME-type values are checked just by examining the 3-letter file extension. Obviously this is easy to change! A more robust method to determine MIME-type is to actually examine the file contents. This may sound complicated, but luckily PHP has a built-in function for doing exactly that, called finfo. Please note that this extension is only available on PHP 5.3 and higher.
XAMPP has this function turned off by default, so if you are on Windows you’ll have to do some additional set. If you are using XAMPP, complete the following sidebar before proceeding.
Xampp Users Only: Activating finfo
Switch to the XAMPP Control Panel.
To the right of Apache, click the Config button and from the menu choose PHP (php.ini).
This will open up php.ini in your default text editor.
Press Ctrl–F to open up the Find window.
Next to Find what, type fileinfo and click Find Next.
-
You should be taken to the following line of code:
;extension=php_fileinfo.dll
-
To turn this feature on, delete the semi-colon (;) at the beginning of the line:
extension=php_fileinfo.dll
Save the file.
Close the file.
You should be back in the XAMPP Control Panel. We need to restart the Apache server for the change to take effect. If Apache is installed as a service, you will also need to restart your computer.
To the right of Apache, click the Stop button.
To the right of Apache, click the Start button.
If Apache does not start up (or it starts then immediately stops), you will need to restart your computer. After you restart your computer, relaunch XAMPP. The servers should start up without a problem.
In your code editor, open upload.php from the phpclass folder.
-
Just above the switch expression, add the following bold code:
$file_info = new finfo; switch($_FILES['myFile']['type']) {
This initializes the finfo method and saves into a new object called $file_info.
-
Let’s use the new object. Add the following bold code:
$file_info = new finfo; $mime_type = $file_info->buffer(); switch($_FILES['myFile']['type']) {
This sets a new variable called
$mime_type
. We also use the$file_info
object and run the methodbuffer()
on it. You can think of an object like a little application that you can run functions on. The -> arrow says to run the functionbuffer()
. -
Next we need to specify the parameters for the
buffer()
function. We need to send it the actual file as the first parameter, and the second parameter will tell it what info we want to get from the file. In this case, we want to get the mime type. Add the following bold code:$file_info = new finfo; $mime_type = $file_info->buffer(file_get_contents($_FILES['myFile']['tmp_name']), FILEINFO_MIME); switch($_FILES['myFile']['type']) {
In order to get the actual file we need to use the
file_get_contents()
function. Inside that we send it the temporary file that PHP has created. The second parameter forbuffer()
is FILEINFO_MIME which tells PHP that we want the MIME type. -
Let’s echo
$mime_type
to see what we have so far. Add the following bold code:$file_info = new finfo; $mime_type = $file_info->buffer(file_get_contents($_FILES['myFile']['tmp_name']), FILEINFO_MIME); echo $mime_type; exit;
-
Save the page and then in a browser go to:
- Mac: localhost:8888/phpclass/upload.php
- Windows: localhost/phpclass/upload.php
-
Click the button to choose a file, then navigate to:
- Mac: Hard Drive > Applications > MAMP > htdocs > phpclass > files
- Windows: C: > xampp > htdocs > phpclass > files
Double–click bird.jpg.
-
Click Upload. You’ll see:
image/jpeg; charset=binary
If you are on Windows, and you see an error that begins with Fatal error: Class ‘finfo’ not found in… make sure to restart your computer after completing the sidebar earlier in the exercise for activating the finfo function in XAMPP.
Switch back to your code editor.
Delete the echo statement. We’re done with it.
-
We want to get only the first part of this string. Our switch statement is expecting just the first part, not the charset=binary. One way to get just the first part is to turn the string into an array using the
explode()
function. We can turn it into a two-part array with the semi-colon as a delimiter. Add the following bold code:$file_info = new finfo; $mime_type = $file_info->buffer(file_get_contents($_FILES['myFile']['tmp_name']), FILEINFO_MIME); $myMimeArr = explode(';',$mime_type); exit;
This makes a new array called
$myMimeArr
. -
Let’s quickly print
$myMimeArr
to see what we have. Add the following bold code:$file_info = new finfo; $mime_type = $file_info->buffer(file_get_contents($_FILES['myFile']['tmp_name']), FILEINFO_MIME); $myMimeArr = explode(';',$mime_type); print_r($myMimeArr); exit;
-
Save the page and then in a browser go to:
- Mac: localhost:8888/phpclass/upload.php
- Windows: localhost/phpclass/upload.php
-
Click the button to choose a file, then navigate to:
- Mac: Hard Drive > Applications > MAMP > htdocs > phpclass > files
- Windows: C: > xampp > htdocs > phpclass > files
Double–click bird.jpg.
-
Click Upload. You’ll see:
Array ( [0] => image/jpeg [1] => charset=binary )
Perfect. Now we can just use the first element in the array.
Switch back to your code editor.
Delete the
print_r
function and the exit statement, too.-
Edit the switch statement as shown in bold:
switch($myMimeArr[0]) {
-
Save the page and then in a browser go to:
- Mac: localhost:8888/phpclass/upload.php
- Windows: localhost/phpclass/upload.php
-
Click the button to choose a file, then navigate to:
- Mac: Hard Drive > Applications > MAMP > htdocs > phpclass > files
- Windows: C: > xampp > htdocs > phpclass > files
Double–click uploadme.txt.
-
Click Upload.
You should see an error message: That file is not the correct type.
Hit the back button in the browser and this time choose a JPG, GIF, or PNG from the folder. The file should upload (check for it in the upload_test folder).
Switch back to your code editor, and close any files you may have open.