Necessity is the mother of invention, and in the virtual world, equipping oneself with the requisite skills to manipulate data holds paramount significance. This guide will elucidate a step-by-step process of how to upload an image to a server using AJAX, validate it with PHP, save it in the server, and then store the image path in the database for future retrieval. To fortify website security, we’ll use MYSQLI prepared statements to save the path in the database.
Safety should always be at the forefront of any web development endeavor. It’s crucial to comprehend that enabling user uploads onto your server can engender considerable security risks, should validation be mishandled. Therefore, meticulous attention must be allocated to validation, the cornerstone of secure file uploads.
Constructing the HTML Form
Embarking onto our journey, we commence with the creation of our HTML form, serving as the interaction interface for our users.
<html>
<head>
<title>Efficient Image Processing with AJAX, PHP, and MySQL</title>
</head>
<body>
<form onsubmit="initiateImageUpload(event);">
<input type="file" name="chosenImage" id="image-picker" accept="image/*">
<input type="submit" name="upload" value="Start Image Upload">
</form>
<div id="loader" style="display:none;">Image upload in progress...</div>
<img id="imageDisplay">
</body>
</html>
In the form above, the initiateImageUpload(event) function is assigned as the value of the onsubmit attribute. The form encompasses two crucial elements:
- A file selection input: This element facilitates user interactivity by allowing them to choose an image file. The accept=”image/*” attribute instructs the browser to only permit image files, thereby ensuring that the user selects an appropriate file type and presenting the user with the suitable app for image selection (especially on mobile OS like Android). Our id attribute, “image-picker,” will be beneficial later on when we need to select this element within our Javascript code. However, the name attribute is optional since we’re not directly submitting this form;
- A form submission button: Clicking this button triggers the form submission and, consequently, the initiateImageUpload(event) function call. This function will cater to the form submission event details, which the browser establishes in memory (the creation of this function will be covered in the subsequent steps).
Supplementary to these constituents, we have an additional two elements whose function is to display the text “Image upload in progress…” and to preview the uploaded image. For simplification throughout the tutorial, we’ll refer to these adjuncts as ‘User Interface (UI) handling elements.’
Implementing Image Upload with AJAX
In this phase, we compose the Javascript code to steer the UI and facilitate image upload, employing AJAX. We start by appending a <script> tag just before the </body> tag of our HTML markup.
<script type="text/javascript">
// Your code will be added here
</script>
</body>
</html>
Placing the Javascript code before the </body> tag is a standard practice. It ensures that the code is executed only after the HTML elements are loaded on the webpage, thereby assuring the accurate execution of functions like getElementById, getElementsByClassName, etc.
Looking back at our form, we note that the function, initiateImageUpload(event), is triggered when the form is submitted, as defined within the form’s onsubmit attribute. Let’s now create this function.
In the function initiateImageUpload(event), we first prevent the form’s default submission process with event.preventDefault();. This is followed by initiating the uploadImage() function, which will handle the image upload process.
function initiateImageUpload(event) {
// Prevent default form submission
event.preventDefault();
uploadImage();
}
The uploadImage() function, though appearing complex, can be dissected into simple parts for a detailed understanding.
We commence by defining a function that uploads the image.
function uploadImage() {
var imagePicker = document.getElementById("image-picker"),
selectedFile = imagePicker.files[0];
if (!selectedFile)
return alert("Please select a file");
// Clear the previous image
imageDisplay.removeAttribute("src");
// Display the loading text
loader.style.display = "block";
// Create form data and append the file
var formData = new FormData();
formData.append("image", selectedFile);
// Implement the AJAX part
var ajax = new XMLHttpRequest();
ajax.onreadystatechange = function() {
if (this.readyState === 4 && this.status === 200) {
var jsonResponse = JSON.parse(this.responseText);
if (!jsonResponse || jsonResponse.status !== true)
return uploadError(jsonResponse.error);
showImage(jsonResponse.url);
}
}
ajax.open("POST", "upload.php", true);
ajax.send(formData);
}
Let’s venture further into each line of the uploadImage() function to achieve a better understanding.
var imagePicker = document.getElementById("image-picker"),
selectedFile = imagePicker.files[0];
if (!selectedFile)
return alert("Please select a file");
The imagePicker variable holds the HTML element responsible for file selection. The files property of this element is an array of selected files. Since we only allow one file to be selected in our form, our required file lies at the 0th index of this array. We store this file in the selectedFile variable. If the selectedFile variable evaluates to false, it means that no file has been selected by the user. In such a scenario, we return from the function with an alert message prompting the user to select a file.
// Clear the previous image
imageDisplay.removeAttribute("src");
// Display the loading text
loader.style.display = "block";
In this part, we remove any previously displayed image and show the “loading…” message to signal to the user that the upload process is happening.
// Create form data and append the file
var formData = new FormData();
formData.append("image", selectedFile);
To facilitate the upload of an image using AJAX, we create a FormData object and append the user-selected image to it. The FormData object is a built-in object in Javascript. The first line of the code snippet above creates an instance of the FormData object. Subsequently, we append our image file to the formData instance. In the append method, the first argument is the name of the parameter, and the second is the file. If you wish to send additional parameters such as client ID with the AJAX request, use the same append method.
var ajax = new XMLHttpRequest();
ajax.onreadystatechange = function() {
if (this.readyState === 4 && this.status === 200) {
var jsonResponse = JSON.parse(this.responseText);
if (!jsonResponse || jsonResponse.status !== true)
return uploadError(jsonResponse.error);
showImage(jsonResponse.url);
}
}
ajax.open("POST", "upload.php", true);
ajax.send(formData);
In the first line, we instantiate the XMLHttpRequest object and assign it to the ajax variable. We then declare the onreadystatechange function for our AJAX request. This function is called every time the ready state of our AJAX request changes. Each time this function is called, it checks whether this.readyState is 4 and this.status is 200. If these conditions are met, it suggests that our AJAX request has been successfully executed.
Once the AJAX request is successful, we parse some JSON data returned by PHP. We then check if this returned JSON data jsonResponse is valid and if the status of jsonResponseis true. If the JSON data is invalid or the status is not true, then we call the uploadError function passing the error from the jsonResponse. If the JSON data is valid and status is true, we call the showImage function passing the url from the jsonResponse.
The AJAX request is then made to upload.php through a POST request with the formData instance being sent as the data. This is done in the last two lines of this section of the code.
It’s noteworthy that we don’t need to set the “Content-type” as “application/x-www-form-urlencoded” when we’re employing the FormData object to dispatch data.
The code for the uploadError and showImage functions is as follows:
function uploadError(error) {
// Execute on error occurrence
alert(error || 'An error occurred.');
}
function showImage(url) {
imageDisplay.src = url;
loader.style.display = "none";
}
The uploadError function generates an alert with the error message passed into it, or a default error message in the case that the error parameter does not evaluate to true. The showImage function displays the uploaded image and hides the “loading…” text.
Congratulations! We just accomplished the AJAX part of our code. Next, let’s lay down the back-end using PHP.
Crafting the AJAX Request Handler with PHP
Choosing the Optimal Method
This tutorial provides an in-depth guide on how to create an AJAX request handler. The chosen approach involves coding the handler within a try-catch block, throwing exceptions in case of errors.
Here is the basic structure of the method:
try {
// code here
if ($anythingFails) {
throw new Exception('Error message');
}
// if code is successful
exit(json_encode(
array (
'status' => true,
'additionalData1' => '...',
'additionalData2' => '...',
)
));
} catch (Exception $e) {
exit(json_encode(
array (
'status' => false,
'error' => $e->getMessage()
)
));
}
If the uploaded image is invalid, the method throws an exception and ends the script, generating a JSON response. This response has a status key showing the request status (false in this case) and an error key storing the error message. The $e->getMessage() function returns the string sent as the first parameter of the throw new Exception() statement.
If the process is successful, the script ends and outputs a JSON response with a status set to true and additional data sent to the client-side, such as the URL of the saved image.
Crafting the Upload Handler
Here is a detailed code for the final upload handler:
$host = 'localhost';
$user = 'user';
$password = 'password';
$database = 'database';
$mysqli = new mysqli($host, $user, $password, $database);
try {
if (empty($_FILES['image'])) {
throw new Exception('The image file is missing');
}
$image = $_FILES['image'];
// check INI error
if ($image['error'] !== 0) {
if ($image['error'] === 1) {
throw new Exception('Maximum upload size exceeded');
}
throw new Exception('Image uploading error: INI Error');
}
// check if the file exists
if (!file_exists($image['tmp_name'])) {
throw new Exception('Image file is missing in the server');
}
$maxFileSize = 2 * 10e6; // in bytes
if ($image['size'] > $maxFileSize) {
throw new Exception('Maximum size limit exceeded');
}
// check if the uploaded file is an image
$imageData = getimagesize($image['tmp_name']);
if (!$imageData) {
throw new Exception('Invalid image');
}
$mimeType = $imageData['mime'];
// validate mime type
$allowedMimeTypes = ['image/jpeg', 'image/png', 'image/gif'];
if (!in_array($mimeType, $allowedMimeTypes)) {
throw new Exception('Only JPEG, PNG and GIFs are allowed');
}
// the file is a valid image
// get file extension (ex: jpg, png) not (.jpg)
$fileExtention = strtolower(pathinfo($image['name'], PATHINFO_EXTENSION));
// create random name for the image
$fileName = round(microtime(true)) . mt_rand() . '.' . $fileExtention; // anyfilename.jpg
// Create the path starting from DOCUMENT ROOT of the website
$path = '/examples/image-upload/images/' . $fileName;
// file path in the computer - where to save it
$destination = $_SERVER['DOCUMENT_ROOT'] . $path;
if (!move_uploaded_file($image['tmp_name'], $destination)) {
throw new Exception('Error in moving the uploaded file');
}
// create the url
$protocol = stripos($_SERVER['SERVER_PROTOCOL'], 'https') === true ? 'https://' : 'http://';
$domain = $protocol . $_SERVER['SERVER_NAME'];
$url = $domain . $path;
$stmt = $mysqli->prepare('INSERT INTO image_uploads (url) VALUES (?)');
if ($stmt && $stmt->bind_param('s', $url) && $stmt->execute()) {
exit(
json_encode(
array(
'status' => true,
'url' => $url
)
)
);
} else {
throw new Exception('Error in saving into the database');
}
} catch (Exception $e) {
exit(
json_encode(
array (
'status' => false,
'error' => $e->getMessage()
)
)
);
}
Exploring the Upload Procedure
Connecting to the Database
For storing the URL in a MYSQL database, a connection to the database is required. It’s useful to keep such declarations at the top of a PHP script.
$host = 'localhost';
$user = 'user';
$password = 'password';
$database = 'database';
$mysqli = new mysqli($host, $user, $password, $database);
After this, all the upload validations should be done inside a try block.
Checking the Image Upload
If the uploaded files are stored in the $_FILES superglobal array, they use the name given in the FormData object to locate the file within the $_FILES array. The file is then saved into a variable for easier access later in the code.
if (empty($_FILES['image'])) {
throw new Exception('Image file is missing');
}
$image = $_FILES['image'];
Understanding the $image Variable
It’s crucial to understand the $image array, which contains data about the uploaded image:
- $image[‘tmp_name’] – The absolute path of the temporary file created in the server;
- $image[‘name’] – The name of the original file (This is sent by the browser and is set to the file’s name when it’s on the user’s computer.);
- $image[‘error’] – An integer representing any error occurred in uploading;
- $image[‘size’] – The size of the file.
Check for Upload Time Errors
If there’s no upload time error, $image[‘error’] will have the value 0. But, if there’s an error, we can’t run further validations, because the image uploading hasn’t been successful. A common error that occurs when uploading is exceeding the upload_max_filesize defined in the php.ini file. In this error, $image[‘error’] will have the value 1. By checking it, we can send the client side a message like ‘Max upload size exceeded’. If $image[‘error’] is not 0 or 1, it will be any other error. (Refer the manual to learn more about the other errors.)
if ($image['error'] !== 0) {
if ($image['error'] === 1) {
throw new Exception('Max upload size exceeded');
}
throw new Exception('Image uploading error: INI Error');
}
Checking the Uploaded File
The file_exists() function is used to check whether the uploaded file exists on the server. The file_exists() function can be used.
if (!file_exists($image['tmp_name'])) {
throw new Exception('Image file is missing in the server');
}
Here, we use ‘tmp_name’, not ‘name’, because the file path of the file saved temporarily in the server is stored in ‘tmp_name’. The ‘name’ element only holds a string of the original name of the file which the user had on his computer.
Checking the File Size
It’s important to prevent users from uploading unlimited size files to the server.
$maxFileSize = 2 * 10e6; // = 2 000 000 bytes = 2MB
if ($image['size'] > $maxFileSize) {
throw new Exception('Max size limit exceeded');
}
Validating the Image
Once the file has been uploaded successfully and the file size is within limit, we need to check whether the uploaded file is a real image. The getimagesize returns data about the image. If it returns false, it means that the file is not a valid image.
$imageData = getimagesize($image['tmp_name']);
if (!$imageData) {
throw new Exception('Invalid image');
}
The getimagesize() function returns the mime type of the image. ($imageData[‘mime’]) We will use that in the next step.
Validating the Mime Type
Even if it is an image, it can be any type among various image types. Restricting the users only to upload certain types of images is a good idea to prevent exploits. Here we only allow the user to upload jpg, png and gif images. You can change it according to your needs.
$mimeType = $imageData['mime'];
$allowedMimeTypes = ['image/jpeg', 'image/png', 'image/gif'];
if (!in_array($mimeType, $allowedMimeTypes)) {
throw new Exception('Only JPEG, PNG and GIFs are allowed');
}
We save the mime type of the image which we got from getimagesize() function in $mimeType variable. Then, we add allowed mime types into an array. Finally, we check whether the mime type of the uploaded image is in this array using the in_array function. Otherwise, we throw an exception and exit the script.
Finding the extension of the file
$fileExtention = strtolower(pathinfo($image['name'], PATHINFO_EXTENSION));
The pathinfo() function with the PATHINFO_EXTENSION flag will return the extension of the file. Here we use $image[‘name’] because the original file name is stored in $image[‘name’], so it has the correct extension. $image[‘tmp_name’] has the extension of the temporary file. In most cases, it is .tmp, therefore $image[‘tmp_name’] cannot be used to find the extension.
Generating a random and unique file name
Using the current timestamp is a common and useful way to generate a unique file name. The microtime() function returns the current UNIX timestamp in microseconds. Adding a unique number (generated with mt_rand() function) can ensure the uniqueness more.
$fileName = round(microtime(true)) . mt_rand() . '.' . $fileExtention; // 3240930420523.jpg
Defining the place to save the image in the server
$path = '/uploads/' . $fileName;
$destination = $_SERVER['DOCUMENT_ROOT'] . $path;
The move_uploaded_file() function is used to move the temporary file into the destination.
if (!move_uploaded_file($image['tmp_name'], $destination)) {
throw new Exception('Error in moving the uploaded file');
}
Creating the URL
Finding the protocol and domain name and adding it in front of the $path variable will return the URL we need.
$protocol = stripos($_SERVER['SERVER_PROTOCOL'], 'https') === true ? 'https://' : 'http://';
$domain = $protocol . $_SERVER['SERVER_NAME'];
$url = $domain . $path;
Ensuring Data Integrity with Required Fields
In the context of image uploads via AJAX in PHP, marking fields as “required” in PHP forms is vital. It enforces data validation, improves user experience, and enhances security. By making sure users provide essential information, such as selecting an image, you prevent incomplete submissions and reduce the risk of security vulnerabilities. Always consider using required fields and robust validation for a safer and more user-friendly web experience.
Let’s sum it up
This tutorial represents a detailed guide on how to insert an image in a database using AJAX in PHP, and it is intended to help you boost your web development skills. The process involves multiple steps starting from establishing a database connection, checking the image upload, validating the image and the mime type, finding the extension of the file, generating a random and unique file name, defining the place to save the image in the server, creating the URL, adding the URL to the database, and ending with sending a JSON response to the client-side including the URL of the image.