PHP has excellent built-in support for handling file upload and validation logic.
We can create a simple, functional file upload script in under 5 minutes.
However, while the uploader is relatively easy, other tasks, such as fast file delivery to a client machine through a CDN or detecting infected or malicious files, will require more complex solutions.
And it’s also essential to consider security risks and other malicious uses of the upload form.
In this article, we’ll cover two different approaches to implementing the uploading functionality and reveal their pros and cons:
Upload files onto your own server and develop a PHP script from scratch.
Use a third-party service like Uploadcare that perfectly integrates with any PHP application to upload, store, and deliver files through a CDN.
To follow along with this article, you need a PHP 8.x or later installed.
You can either use a server like nginx and Apache or try the demo with PHP’s built-in development server.
By default, PHP is configured to allow file uploading and has sensible defaults for the maximum number of files and file sizes that can be uploaded.
If you need to troubleshoot file uploading or adjust any limits, you can do this in the php.ini configuration file.
The most relevant file upload configuration options are shown here:
;;;;;;;;;;;;;;;;; File Uploads ;;;;;;;;;;;;;;;;;; Whether to allow HTTP file uploads.file_uploads=Onupload_tmp_dir=/path/to/your/tmp/folder; Maximum allowed size for uploaded files.upload_max_filesize=2M; Maximum number of files that can be uploaded via a single requestmax_file_uploads=20
Do not forget to set the upload_tmp_dir value according to your own path.
HTML provides a file input type to select a file from a local computer.
The input has several attributes for configuring the allowed file types.
The input also requires setting some corresponding attributes on the HTML form. You can experiment with the allowed file types or allow selecting multiple files.
However, the most important detail for our needs is an enctype attribute that should be set to multipart/form-data.
That way, we tell a browser that our form contains binary data that should be encoded for transmission.
Also, we should send a POST request by setting the method attribute with the relevant value.
When the user submits the form, the browser sends the file to the server using a single request.
Depending on the user’s upload connection speed and the file size, this upload request could take quite some time.
Setting reasonable defaults will prevent the request from timing out.
First, we need to take care of the client-side code in HTML and create a form to allow users to select files for uploading.
We’ll keep this form very basic, but you might want to set attributes to limit file types, file sizes, and more in a real-world application.
Create a new index.html file within your sample web application. Copy the following code into your newly created file.
It features a single file select input and a submit button.
As you can see, we’ve also made the HTML form with the proper enctype and method attributes to support binary file uploads.
<!DOCTYPEhtml><html><body><h1>PHP File Uploader</h1><formaction="uploadHandling.php"method="post"enctype="multipart/form-data">
Select an image to upload:
<inputtype="file"name="fileToUpload"id="fileToUpload"><inputtype="submit"value="Upload"name="submit"></form></body></html>
The resulting form should look like this:
Now, our form uses default styling. Of course, you can write your own CSS for the uploader to make it look nice.
The form’s action attribute refers to the script that will receive and process the uploaded file on the server side.
The next step for us is to develop this script!
The server script name should match the name we mentioned in the HTML. Create a folder called uploads in the directory of your project.
It’ll store the incoming files.
Copy and paste this uploadHandling.php script as a server-side solution for your file/image uploader.
We provided some comments to explain the basic functionality of each code section.
In this sample code, we’re only allowing certain image file types to be uploaded and limiting the file size to 5 megabytes.
<?phpdeclare(strict_types=1);// redirect to upload form if no file has been uploaded or an error occurredif(!isset($_FILES['fileToUpload'])||$_FILES['fileToUpload']['error']>0){header('Location: index.html');exit;}// configuration$uploadDirectory=getcwd().'/uploads/';$fileExtensionsAllowed=['jpeg','jpg','png'];// These will be the only file extensions allowed$mimeTypesAllowed=['image/jpeg','image/png'];// These will be the only mime types allowed$maxFileSizeInBytes=1024*1024*5;// 5 megabytes// extract data about the uploaded file['name'=>$fileName,'tmp_name'=>$fileTempName,'type'=>$fileType,'size'=>$fileSize]=$_FILES['fileToUpload'];['extension'=>$fileExtension]=pathinfo($fileName);$errors=[];// validate the file extension is in our allow listif(!in_array($fileExtension,$fileExtensionsAllowed,strict:true)){$errors[]='File must have one of the following extensions: '.implode(', ',$fileExtensionsAllowed).'.';}// validate the file is an allowed mime type based on actual contents$detectedType=mime_content_type($fileTempName)?:'unknown type';if(!in_array($detectedType,$mimeTypesAllowed,strict:true)){$errors[]='File must have one of the following mime types: '.implode(', ',$mimeTypesAllowed).'.';}// validate if the file already exists$uploadPath=$uploadDirectory.$fileName;if(file_exists($uploadPath)){$errors[]='File with the same name already exists.';}// validate the maximum file sizeif($fileSize>$maxFileSizeInBytes){$errors[]='File must not be greater than '.number_format($maxFileSizeInBytes).' bytes.';}// verify for errors and move the file upon successful validationif(count($errors)>0){echo'<h3>Errors</h3>';echo'<ul>';foreach($errorsas$error){echo'<li>'.$error.'</li>';}echo'</ul>';exit;}if(move_uploaded_file($fileTempName,$uploadPath)){echo'The file has been successfully uploaded.';}else{echo'There was an error uploading your file.';}
When our PHP application receives file uploads, it populates a global $_FILES object with the uploaded file contents and some file properties, such as name, size, and error status.
Note that we use PHP’s built-in fileinfo extension to determine the actual MIME type for validation because, in real life, you can’t trust the provided file extension from users.
We also received both the original name of the file as well as the temporary name PHP assigned.
All uploaded files are placed into a temporary upload directory. If validation passes, and ONLY if it passes, we move the temporary file to the desired location.
For purposes of this example, we’re not allowing a user to upload a file with a name that already exists.
In a real production application, you’d likely sandbox user uploads per account or add additional logic to prevent overwriting existing files.
In regards to file size validation, don’t forget that the limits we’re enforcing in this PHP script are separate from any limits enforced in the php.ini configuration, or in your web server configuration.
The complete source code is available in this GitHub repo.
This script provides a basic uploading functionality.
There are many other tasks related to the file uploading process, such as distributing files across servers for optimal delivery, image processing (e.g., crop and other effects), malicious file detection, etc.
If you need a fast, reliable, and robust solution for your image uploads, you can use a third-party solution.
For example, let’s take Uploadcare and use its functions directly from our PHP application.
It’ll give you more reliability, fast uploads, and much more.
Also, you can bypass PHP altogether and embed Uploadcare methods through HTML and JavaScript.
If you want to try the approach, you can check another step-by-step guide for implementing Uploadcare functionality via JavaScript.
In this tutorial, we’ll continue with Uploadcare PHP API client, which lets you handle uploads and file operations within your PHP app.
Let’s start with a short resume of implementing this approach.
Pros
Super-fast setup and optimized upload speed.
Manages storage and works with CDN automatically.
Additional features include resizing, cropping images, inappropriate image detection, etc.
Stronger security protection, including virus scanning.
Technical support if you ever run into a problem.
Cons
Some features are not free, but you can choose the packages according to your business’s needs and budgets.
While Uploadcare stays as light dependency as possible, it is a third-party service that increases your project size.
Sign up to Uploadcare. Once you have an account, create a project to store your images.
Then, you’ll be able to get your project’s API keys.
The Public key helps identify your project from another website or app, and a Secret key is required when using Uploadcare REST API features.
First, let’s copy and paste an index.html file with an uploading form from our previous project:
<!DOCTYPEhtml><html><body><h1>PHP File Uploader with Uploadcare</h1><formaction="uploadHandling.php"method="post"enctype="multipart/form-data">
Select an image to upload:
<inputtype="file"name="fileToUpload"id="fileToUpload"><inputtype="submit"value="Upload"name="submit"></form></body></html>
Then, create an uploadHandling.php script and include Composer’s autoload file and add the Uploadcare dependencies:
First, create an instance of the Uploadcare\Api class by passing the configuration object to it.
Then, call the uploader() method on the instance to get the uploader object.
After that, call the fromPath() method on the uploader object to upload the image to Uploadcare. This method accepts four arguments:
The file path on your local device.
The desired filename.
The MIME type of the file.
UPLOADCARE_STORE option is set to ’auto’ in this instance, allowing Uploadcare to decide the most suitable storage strategy.
You might notice that we added some rules and restrictions regarding file size and type in the previous section.
With Uploadcare, you can do the same and even more without additional coding. Let’s see how it works.
Open your project settings to find all the file validation and moderation parameters.
You can easily change the maximum file size by editing the related field. In a free Uploadcare plan, you can upload files up to 500 MB.
Also, you can control an uploaded file type.
While your account is unverified, you can upload only images. However, providing your debit or credit card details lets you choose any file type you need.
It’s important to note that upgrading to a paid plan is unnecessary to start using this feature.
Unfortunately, there is no way to fine tune MIME types through the dashboard, but it’s available on your backend side by simple modifications of our fileInfo object:
So, that’s it. There are many more Uploadcare features you can set up through the dashboard.
Don’t hesitate to explore it with Uploadcare Getting Started guides.
Creating a custom upload script provides complete control and customization options but demands a substantial time investment for development, scalability, and security measures.
It’s a free solution, assuming no external services.
Uploadcare offers a rapid setup, optimized upload speed, and a professional UI that’s easily customizable.
It manages storage and leverages CDN for fast delivery.
It comes with features like resizing, cropping, and content detection. Additionally, it provides robust security measures and support.
Consider your project’s specific requirements, resources, and scalability needs when deciding.
For a fast, reliable, and feature-rich solution seamlessly integrating with PHP, Uploadcare stands out.
It simplifies the process and enhances security, performance, and user experience.
After all, why walk to the airport when you can take a cab?