PHP Ajax upload for images and files

Posted by Olaf #

The asynchronous file upload, using some XMLHttpRequest (Ajax), is technically not possible. Most JavaScript examples and tutorials call this method still Ajax upload and the image or file is uploaded by using a "virtual IFRAME". Anyway it's still user friendly way to provide an upload function for your visitors or users.

In this quick tutorial I will show your how-to create such an Ajax image upload form using the jQuery Form plug-in and my PHP upload class. There is also an Ajax upload demo page which will show how it really works.

The scripts are very simple, build your image upload form just like normal. In place of posting the form data to a PHP script, you will use some JavaScript code to post the data to a PHP script in the background.

Update from 2014-11-21:
There was a validation problem in the previous versions of my PHP upload class (< 2.35). I advice to update the class script, for the code from this tutorial and for all other scripts using this class.

Requirements

Download the required files and place the JavaScript files into the same directory as your HTML document and place the following JavaScript code into the HTML header:

<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="jquery.form.js"></script>
<script type="text/javascript">

$(document).ready(function() {
	$("#loading")
	.ajaxStart(function(){
		$(this).show();
	})
	.ajaxComplete(function(){
		$(this).hide();
	});
	var options = {
		beforeSubmit:  showRequest,
		success:       showResponse,
		url:       'upload4jquery.php',  // your upload script
		dataType:  'json'
	};
	$('#Form1').submit(function() {
		document.getElementById('message').innerHTML = '';
		$(this).ajaxSubmit(options);
		return false;
	});
}); 

function showRequest(formData, jqForm, options) {
	var fileToUploadValue = $('input[@name=fileToUpload]').fieldValue();
	if (!fileToUploadValue[0]) {
		document.getElementById('message').innerHTML = 'Please select a file.';
		return false;
	} 

	return true;
} 

function showResponse(data, statusText)  {
	if (statusText == 'success') {
		if (data.img != '') {
			document.getElementById('result').innerHTML = '<img src="/upload/thumb/'+data.img+'" />';
			document.getElementById('message').innerHTML = data.error;
		} else {
			document.getElementById('message').innerHTML = data.error;
		}
	} else {
		document.getElementById('message').innerHTML = 'Unknown error!';
	}
} 

</script>

Next create a PHP script named "upload4jquery.php" and place it in the same directory where the other files are located. Place this code into your PHP file:

<?php
include($_SERVER['DOCUMENT_ROOT'].'/classes/upload/foto_upload_script.php');

$foto_upload = new Foto_upload;	

$json['size'] = $_POST['MAX_FILE_SIZE'];
$json['img'] = '';

$foto_upload->upload_dir = $_SERVER['DOCUMENT_ROOT']."/upload/";
$foto_upload->foto_folder = $_SERVER['DOCUMENT_ROOT']."/upload/";
$foto_upload->thumb_folder = $_SERVER['DOCUMENT_ROOT']."/upload/thumb/";
$foto_upload->extensions = array(".jpg", ".gif", ".png");
$foto_upload->language = "en";
$foto_upload->x_max_size = 480;
$foto_upload->y_max_size = 360;
$foto_upload->x_max_thumb_size = 120;
$foto_upload->y_max_thumb_size = 120;

$foto_upload->the_temp_file = $_FILES['fileToUpload']['tmp_name'];
$foto_upload->the_file = $_FILES['fileToUpload']['name'];
$foto_upload->http_error = $_FILES['fileToUpload']['error'];
$foto_upload->rename_file = true; 

if ($foto_upload->upload()) {
	$foto_upload->process_image(false, true, true, 80);
	$json['img'] = $foto_upload->file_copy;
} 

$json['error'] = strip_tags($foto_upload->show_error_string());
echo json_encode($json);
?>

This tutorial or guide is not about how to use the PHP upload class. If you never used the class before, than try the example files first and try than the Ajax upload form.

Paths and upload directories

You need to create two upload directories: One for the upload main file and one for the thumbnails. Check/change the permission for the directories (CHMOD the directories with 0755). If you use the same structure as suggested in the PHP class file, there is no need to change the includes at the top of the PHP script.

Now we need to create the form HTML and some other containers where the response data will be placed.

<form id="Form1" name="Form1" method="post" action="">
    <input type="hidden" name="MAX_FILE_SIZE" value="<?php echo $max_size; ?>" />
    Select an image from your hard disk:

    <div>
        <input type="file" name="fileToUpload" id="fileToUpload" size="18" />
        <input type="Submit" value="Submit" id="buttonForm" />
    </div>
</form>
<img id="loading" src="loading.gif" style="display:none;" />

<p id="message">

<p id="result">

The file loading.gif is the upload indicator image, pick the file I've used on the demo page or check Google for other stylish images or use an online image generator.

Some final note, the code works as it is. Don't change variable names or form field attributes, if you're not sure how to change them inside your JavaScript code.

Comments / discussions

Posted by Jet #

Hi Olaf,

As usual, everything works out of the box. The only minor exception being, it seems, that the latest classes zip file now contains an extra file - foto_upload_script.php

The Easy Upload Class plus your instructions above form a very good and complete set of PHP, Mysql Ajax upload scripts.

Thank you Olaf.

Posted by Olaf #

Yes right this file is without the example code, I will split them for good and post an update. Thanks for mentioning.

Posted by 90210 #

include($_SERVER['DOCUMENT_ROOT'].'/classes/upload/foto_upload_script.php');
where can I download this file?

Posted by rizmedh #

@90210
you can download the file with the new version from the PHP Upload class.

I have a problem: My image is uploaded successfully, but the thumb isn't created. And than the uploaded image doesn't show up on my page, please help.

Posted by rizmedh #

thanks, now its work, i was setting image_magick = false .. =)

Posted by 90210 #

How is it possible to upload video files with an .avi and .mpeg extension and not just of image files?

Posted by Olaf #

Hi,

just add other extension to this array:
$foto_upload->extensions = array(".jpg", ".gif", ".png", ".avi");

If you like to show the video content after the upload, you need to change the HTML too (don't use the image tag for videos, maybe you need to add some video player code first).

Posted by bicho44 #

Hi,
Maybe it's a bit to much, but is it possible to upload a .zip file, unzip and move them to another directory and than watermarking the image in one way?

I know, I ask maybe too much, but maybe someone can point me into the right direction.

Posted by Olaf #

Why do you like to zip that upload file first? Is the data size so big?

Posted by bicho44 #

No, it's just a bunch of photos. If you zip them first it's less work to upload them one by one. Don't you think?

Posted by Olaf #

Sure it's easier for the user to upload several files at the same time, but you know that handling lots of files at the same time is more resource intensive for the server. Finally it depends on your application.

After the upload is done, you need to use some Linux commands to unzip the file. Than you need to use some PHP directory functions to loop the directory content (the files). You can use the Imagemagick command line tools for the watermarks.

Posted by Snelmer #

I followed your instructions, but when I try to upload something, the screen just blinks and nothing is uploaded.

My test URL is http://snelmer.nl/upload/

Posted by Olaf #

Seems like that you're missing the half of the HTML code on your page. Maybe is this tutorial to advanced, you can try some basic HTML tutorials first ;)

Posted by Snelmer #

I didn't used any design or something... I'm not new to web-design, but new to PHP

Posted by Olaf #

Quote from: Snelmer
"I didn't used any design or something... I'm not new to web-design, but new to PHP"

Create a full and valid HTML page with all the code you need (check my demo)

Right your didn't used all JavaScript code...

Posted by the_gnoid #

Thanks, Olaf. This is very useful. I have one question though. Is there supposed to be a thumb and an original size image? I'm only getting the thumb version.

Posted by Olaf #

Quote from: the_gnoid
"Is there supposed to be a thumb and an original size image? I'm only getting the thumb version."

Yes, the example creates only one image:

Check the settings for this method (inside the class file):

$foto_upload->process_image(false, true, true, 80);

and don't forget to change the values for these variables:

$foto_upload->upload_dir = $_SERVER['DOCUMENT_ROOT']."/upload/";
$foto_upload->foto_folder = $_SERVER['DOCUMENT_ROOT']."/upload/";
$foto_upload->x_max_size = 480;
$foto_upload->y_max_size = 360;
$foto_upload->x_max_thumb_size = 120;
$foto_upload->y_max_thumb_size = 120;

Posted by the_gnoid #

So, the temporary file is the original file, then? Makes sense. Thanks!

Posted by Olaf #

Quote from: the_gnoid
"So, the temporary file is the original file, then? Makes sense. Thanks!"

Exactly, I did that because I needed only one resized image...

Posted by bicho44 #

Hi,

I was testing this tutorial, but I think the JSON doesn't return the file info...

You can see my demo at
http://www.tratodirecto.com.ar/upload/index2.php

Actually the scripts works, but I never received a response from the server (the function "showResponse" is never fired and I don't know why)

Before this I did a test at
http://www.tratodirecto.com.ar/upload/index.php

and here works the upload, too.

I modified (trying to make it work) a bit the JavaScript, to make it jQuery, but still no response or an error.

Im using the latest jQuery (1.2.6) version and also the latest jQuery forms plugin

Do i miss something?

Posted by Olaf #

Hi,

just tried your php script and noticed that it didn't give a response.

Check all pathes and also the permissions for the upload directory. Your upload page looks fine, you need to debug the upload script.

Posted by bicho44 #

Yup, the script works...

I don't have Imagemagick and the variable is set to false... is that a problem?

It's strange because the 'showResponse' never returns the file info and never got fired. Fore one of my tests, I put an alert into the script to see if something was coming from the upload script.

I'll try it again, but maybe it happen because I'm using a multiple upload function. I like to use this until I'm able to modify the script for watermarks and unzip functionality :D

Posted by Olaf #

Hi,
try the PHP upload script without the Ajax code, check if you get the JSON string (response) from that PHP script.

If you don't have Imagemagick you to turn this function off.

Posted by the_gnoid #

What do I add to convert the file type of the thumb? I'm not very familiar with ImageMagick.

Posted by Olaf #

Hi,

I like Imagemagick very much because it's so easy to use :)

Check this page: http://www.imagemagick.org/script/convert.php

To use the IM command line tools you write some extra code.

Posted by yancho #

Hi,

The script works almost fine, except that I get this after upload: File: me.jpg successfully uploaded! The uploaded file is renamed to 1225052057.jpg.

I cannot find this file, I searched all the directories, but no luck. There are no files in the upload folder :S

These are my paths:

$foto_upload->upload_dir = $_SERVER['DOCUMENT_ROOT']."/upload/";
$foto_upload->foto_folder = $_SERVER['DOCUMENT_ROOT']."/upload/";
$foto_upload->thumb_folder = $_SERVER['DOCUMENT_ROOT']."/upload/thumb/"

You can view my page here: http://exigy.solutions-lab.net/admin/news_mgt.php - any help is extremely appreciated

Thanks Matthew

Posted by Olaf #

Hi,

do you "chmod" the upload directories?

I see also that the location where the image is shown after upload is different from your upload directory:
/files/thumb/1225053135.jpg

Posted by yancho #

Thanks a lot for pointing me on this /files/ thing :)

Seems like that I saved file to the wrong folder. Now it works fine :)

Thanks a lot for your prompt help :)

Can I use this variable to store the file name in a database?

Will $_COOKIE['uploaded_image'] = $fileUpload->file_copy; do the trick?

Posted by Olaf #

SAure you can use this variable right in your SQL statement (don't know why you use a cookie var...)

Posted by yancho #

Hi :)

I will try to pay a bit more for your script... however I found another problem. When I put the form inside another form both submit buttons are submitting the image and not the script. Is there a way to get this done by using the image upload form and maybe some onClick event that fire the other (data) form?

http://exigy.solutions-lab.net/admin/edit_news.php

thanks

Posted by Olaf #

Perhaps, you can use two forms?

Posted by yancho #

I can't, because I like to have the image upload in the middle of my form. Is this possible?

Posted by Olaf #

A form is just another HTML element like a paragraph or header. It depends on the code you're using for the file upload.

Posted by yancho #

So I can use :

<form id="main_form" action="edit.php"> method="post">
bla bla
  <form id="Form1" name="Form1" method="post" action="">
  </form>
</form>

This is currently my code: http://pastebin.ca/1242322

Thanks for prompt replies :)

Posted by Olaf #

No, you can't have nested forms. Place them side by side and style (float) them with CSS.

I see that you use tables, don't use them ;)

Posted by shbashba #

Hi,

Thank you for the wonderful script. Currently I want to put the script into another form and since there is no such nested forms, I can't use "form1" anymore. So can i change the event to onclick or onchange when the user has choose their files then start passing the jquery function?

Thank you.

Posted by Olaf #

Hello,

check the last posts in this thread (it was asked before)

Posted by nlarnold #

Quote from: bicho44
"Yup, the script works... I don't have Imagemagick and the variable is set to false... is that a problem? It's strange because the 'showResponse' never returns the file info and never got fired. Fore one of my tests, I put an alert into the script to see if something was coming from the upload script. I'll try it again, but maybe it happen because I'm using a multiple upload function. I like to use this until I'm able to modify the script for watermarks and unzip functionality :D"

Did you ever find a solution for this? I am having the exact same problem. The script uploads and resizes just fine, creates both images, etc., but no response is posted. I tried to put it together and never got a response. I'm stumped Any insight would be helpful.

Posted by Olaf #

Hi,

if you don't get a response (image), my first guess is that your paths are wrong.

Posted by nat.hagey@gmail.com #

This script is great but I'm having a hard time while installing Imagemagick on Leopard running MAMP any good resources? Or at least a good host that has already Imagemagic installed?

Posted by Olaf #

Hi,

I can advice a shared hosting account from Webfaction, they offer great hosting with Imagemagick (and many other important extensions) enabled. The support is very helpful and their server have a great performance.

Posted by nat.hagey@gmail.com #

Quote from: Olaf
"I can advice a shared hosting account from Webfaction, they offer great hosting with Imagemagick (and many other important extensions) enabled. The support is very helpful and their server have a great performance."

Thanks Olaf! I'll look them up.

Posted by andrew_answer #

I need some small fixes for the Chrome browser which I use:
- first, in upload4jquery.php you should use different directories:

$foto_upload->upload_dir = $_SERVER['DOCUMENT_ROOT']."/upload/";
$foto_upload->foto_folder = $_SERVER['DOCUMENT_ROOT']."/upload/files/";
$foto_upload->thumb_folder = $_SERVER['DOCUMENT_ROOT']."/upload/thumb/";

- second, you should fix the JavaScript showRequest function:

function showRequest(formData, jqForm, options) {
  var fileToUploadValue = document.getElementById('fileToUpload').value;
  if (fileToUploadValue=="") {
    document.getElementById('message').innerHTML = 'Please select a file.';
    return false;
  }
  return true;
}

Posted by Olaf #

Hi,

you say that the "jquery styled" code doesn't work in Chrome?

var fileToUploadValue = $('input[@name=fileToUpload]').fieldValue();

Posted by bt #

Hi
Great code, seems to work after making these Chrome changes from above, although I am using a Mac with Firefox and Safari. I get the showRequest results on my form page, but I do not get anything from the showResponse. It uploads the file and nothing else.

Moreover, sometimes thumbs are created sometimes not. Lastly, where do you control the naming convention for the files, I want to give my files the session IDs as names.

By the way, I inserted an error object for the AJAX request and I keep getting (object Object) as a response from JSON.

Posted by Olaf #

Please show me some of your code or the URL.

Posted by bt #

Olaf

This is running on my local machine so I do not have a URL.

Here is the latest of my script which I saved as upload.js. As you will see, I added an error condition to AJAX request.

$(document).ready(function() {
			$("#loading")
			.ajaxStart(function(){
				$(this).show();
			})
			.ajaxComplete(function(){
				$(this).hide();
			});
			var options = {
				beforeSubmit:  showRequest,
				success:       showResponse,
				url:       'upload4jquery.php',  // your upload script
				dataType:  'json',
				error: function(msg){
				alert( "Houston we have a problem: " + msg );
				}
			};
			$('#Form1').submit(function() {
				document.getElementById('message').innerHTML = '';
				$(this).ajaxSubmit(options);
				return false;
			});
		}); 

		function showRequest(formData, jqForm, options) {
		  var fileToUploadValue = document.getElementById('fileToUpload').value;
		  if (fileToUploadValue=="") {
		    document.getElementById('message').innerHTML = '<img src="images/warning.jpeg" width="15 height="15" /><font color="red">&nbsp; Please select a file.</font>';
		    return false;
		  }
		  return true;
		}
		function showResponse(data, statusText) {
			if (statusText== 'success') {
				if (data.img!= '') {
					document.getElementById('result').innerHTML = '<img src="upload/thumb/'+data.img+'" />';
					document.getElementById('message').innerHTML = data.error;
				} else {
					document.getElementById('message').innerHTML = data.error;
				}
			} else {
				document.getElementById('message').innerHTML = 'Unknown error!';
			}
			/*document.getElementById('message').innerHTML = 'here is what I got';*/

		}

I may send you an email, If you would like more info

Posted by bmorvai #

Hi!

I tried to install on my localhost and on a live server, but i have encountered the same error.
When i try to submit a file, the screen just gives a blink...
Firebug says this after the submit, but disappears very quickly and demands enctype :

[Exception... "'Syntax error, unrecognized expression: [@name=fileToUpload]' when calling method: [nsIDOMEventListener::handleEvent]" nsresult: "0x8057001e (NS_ERROR_XPC_JS_THREW_STRING)" location: "<unknown>" data: no]

Thanks in advance,
scubi

Posted by Olaf #

Hi,

do you tried the PHP upload script without using the Ajax function?

Posted by bmorvai #

The upload works even in the Ajax version. The problem is that I don't receive any response. Not and image or any error message. Without Ajax everything is fine.
I looked in the forum and noticed that someone has the same error.

Posted by Olaf #

That is strange, I use the same code on my upload demo page. Do you see any difference between both scripts? And does the demo work in your browser?

Posted by bmorvai #

I started from the beginning using your scripts.

And the reason why I can't get message and the uploaded picture is :

Permission denied to call method Location.toString

It has to do something with the links, hasn't it?

thanks for your time

Posted by Olaf #

Try the client side (JavaScript) code from the demo I've posted before together with the PHP upload file and the other files from the tutorial. Does the demo works for you?

Posted by bmorvai #

Hi!

I had problems to get a response from my script because of the JSON library. I would advice for others with the same problem, to check the PHP version and and also the JSON library. You need to use PHP 5.2 or higher to have JSON enabled. Otherwise you need to enable the JSON extension by yourself.

Posted by Olaf #

The JSON function in PHP is very limited and simple, you can find also alternative functions in the PHP manual for PHP versions lower than 5.2.

Posted by sladda #

Nice script, it works fine.
Maybe you can help me with this: I want to extend this script a bit, but just adding an echo somewhere, produces problems.
I want to have the whole path of the uploaded file (got it already in a var) and want to read it then with the script phpExcelReader. I've already written this XLS-Read-Script and it works, but I can't join the upload script with my excel-read-script. If I paste any code at the (I think) right position, the upload doesn't work anymore :(

Posted by Olaf #

Hi,

the best way to do this to start with an upload script without the Ajax part. This is the script where you have add/test your Excel process, too. After the PHP/upload script works, use the Ajax part for your script.