WordPress is a great CMS and has a nice feature for uploading files. That is the media uploader which can be found inside the WordPress administration. In this tutorial we will not use the media uploader. We will develop our own WordPress file upload using AJAX and PHP.
The code used in this tutorial can be reused inside a theme or a plugin so feel free to experiment with it and create your own uploader. This tutorial will show you the code that can be used inside the WordPress administration or even on the front end.
There are different approaches on defining how to handle the file uploads:
- using WordPress API – wp_handle_upload which will upload the file using the defined upload structure. Default structure is year/month inside the uploads folder.
- using WordPress API – media_handle_upload which will upload the file and creating an attachment in the database so that the uploaded file can be seen inside the WordPress administration
- using plain PHP to create a custom uploader
The upload form
Let’s assume that we have an upload form where the file will be inserted. This form will be hidden since we will create the upload via AJAX and PHP. To upload a file we will just click on an empty box that will be filled with an image or the filename. Here is our form:
The first element inside the form will be used to append upload messages such as a success message or even a failure message. Next one is the element #ibenic_file_upload and inside that element we will have the input which we will call upon clicking the #ibenic_file_upload.
The other element which is hidden is #ibenic_file_upload_preview and this element will be used to show the uploaded file. It will also have a button to delete the uploaded file.
Styling the form
We will add some simple styling to our form so that it can have a simple and slick look. You can change it on your own later but for now let’s just use this one. I will explain the trick we have used with our CSS.
First we need to enqueue our CSS. You could also just create a style tag before our form so you do add another HTTP request. I am showing you how to enqueue a style so that you can use that knowledge for your own plugin or theme.
The content of our CSS file is:
The two main containers with the class .file-upload were given a property position with the value relative. By declaring such property we can then position other elements inside that containers how we want. Our input is then positioned absolutely on the container with the widht and height set to 100%. In that way we have stretched our input element across the whole container. We did set the opacity to 0 because in that way the input is still clickable even thought we can’t see it.
With that trick we now have a clickable container which will open the pop-up to select a file for upload.
JavaScript and AJAX
The JavaScript written in this chapter can be placed below the form or even enqueued as a separate .js file if you are using this code or your theme or plugin. Here is an example on how to enqueue it inside a plugin:
Here we are setting the path to our file which in this example resides inside the folder js which is inside a folder assets. The file is also dependant of jQuery which we defined inside the array. The last boolean value defines if the script will be placed in the footer or in the header. We are placing it in the footer.
We are also using a WordPress function wp_localize_script that will set a global JavaScript object ibenicUploader with the property ajax_url which will hold the URL to the PHP file for handling AJAX calls.
First we are going to set some defaults before creating AJAX calls. Add this to your javascript file:
Here we are setting a click event on the element #ibenic_file_upload. This function that we are setting makes sure that the input will be clicked and that the window for choosing a file will open up. We are also setting an event trigger when the value of our input changes. This means that when we add a file to our input element, a function named prepareUpload will trigger. This function will prepare our data with the chosen file.
This function will also create an AJAX call on our site.
Here we are setting some defaults for the AJAX call and the success callback function that will be called when the AJAX is successfully completed. This means that the server returned a 200 response. If there was a 40x or 50x error status an error callback function will be called if that function was defined.
In the success callback function we will need to define everything related to showing the file preview and the delete button, but also hiding the current element that we are using for uploading the file. We will now create the rest of the code for the AJAX success even though we do not have any PHP functionality yet.
Here we are checking if the data object (JSON) has a property named response with a value of SUCCESS. If that is the response value then we will check if the data sent is of type image (jpeg, jpg, png or gif). If that is true then we will create an image element with the url to the image. If the file is not an image then we will just show its filename.
The created image or the filename will be appended to the element #ibenic_file_upload_preview. We target it by getting the ID of the first element which is used to upload the file: ibenic_file_upload. We then concatenate it with the string _preview so that we get the ID of the element for previewing the uploaded file.
We are also setting the uploaded filename on the delete button so that when we click on the delete we will know which file to delete. All that data that we are checking and setting (url, filename, response and type) are actually properties that will be set on the backend code with PHP.
The delete button and the delete functionality will be written later in this tutorial.
Reading the POST data
Now we will have to create a function that will be used to read the posted data and save the file on the server. Open your plugins’ or themes’ file where you will add that functionality and add this:
Here we are using the action wp_ajax_ibenic_file_upload which is set by our action sent in the AJAX call. This will work only for the logged in users. If you want to enable any visitor of your site to upload their files then you can do that by adding another action:
add_action("wp_ajax_nopriv_ibenic_file_upload", "ibenic_file_upload");
The only difference is the work nopriv which is added in the action name. Before we begin reading all the POST data we will set a variable with file errors:
Once we have set those errors we can start reading the data. For the first few lines we will read both the global $_POST variable and the $_FILES variable. Then we will merge them together so that we can access all the data under a single variable. Here is the code of that:
At the end of the function we have also added the function die(). Since we are creating a AJAX call on the admin_ajax.php file we do want to stop the reading process when we have already sent the required data as the response. Now with the variable $data we can read all the data that is sent by the AJAX call.
Using the WordPress API wp_handle_upload
Now that we have set our data in a variable, we can move on and start uploading our file. The first example is an example with the WordPress API wp_handle_upload. To read more about this function follow this link.
We are first defining an empty variable response to hold the response. After that we are using the wp_handle_upload function to upload the file. In this function we are sending the data of the file uploaded. Since we are appending that file data to the key ibenic_file_upload in the AJAX call, we are also referencing that data with the same key. The other data that is passed to this function is an array with some overrides. Since we are not sending this directly from a form but from an AJAX call we will not test the form.
After that we are checking if the file was uploaded and also if there was an error. If there was no error, we are sending a SUCCESS response with the data which we have already defined by setting the AJAX success callback function. If you do not remember them, they were:
- Filename
- URL to the file
- File type
If there was an error, then we are just sending the error message with an ERROR response. At the end we are echoing that variable which is encoded in JSON. You can try to upload a file now and see if the image will show in the box or if the filename of another file will be rendered.
Using the WordPress API media_handle_upload
Let’s move on another WordPress upload function. This time, the function is the media_handle_upload. We will use the same function and we will not erase the functionality that we have added before. By doing that you can decide how you will upload your file and also with some other posted data we can decide which uploader to use because there can be different situation which will need a different way of uploading the file.
So at the beginning of our function we are defining a variable that will hold the number of the uploader we want to use. Below we have wrapped both uploader in IF statements. So since we are now using the number 2 uploader we are using the media_handle_upload. If you want to learn more about that function visit this link.
I will now explain only the wrapped code under the uploader number 2.
We are immediately calling the mentioned function where we are passing the string which is the key of our file upload. The second parameter is the post ID to which we want to attach this file. Since we have passed a zero (0) this function will not attach the file to any of the posts.
Once we have used that function we are checking if the variable is a WordPress error and if it is, then we are setting the ERROR response and we are getting the error message from our data variable where we have stored information about the uploaded file. If there is an error with the uploaded file, we will have a different number under the key error. We are then passing that number to the array fileErrors which holds all the error description for each error number.
If the attachment ID is not a WordPress error but a real ID we are getting the full path to the uploaded file. Then we are using a PHP function pathinfo to get some data about that file. From that data we can get the filename and the extension. If the extension is of an image type such as a jpg or png, we are extending the variable type with additional info to match the type in the AJAX success function. In that way, if the file is of extension jpg, jpeg, png or gif we will send image/jpg, image/jpeg, image/png or image/gif and not only the extension.
We are also getting the URL of the uploaded file with the WordPress function wp_get_attachment_url.
Creating a custom WordPress file upload
We will not create our own custom WordPress file upload by uploading our files to the folder custom. This can be defined by you in any way you want. You could also added another layer and upload files to a folder named by the ID of a logged in user or something similar. For this example we will just upload the files to a folder custom.
I will now focus only on the wrapped code for the uploader number 3. Don’t forget to change the number of the variable ibenicUploader to number 3 so that this function can use that uploader.
So at the beginning of that wrapped code, we define several variables where we store information about the path to the upload folder and then using that information we are also creating two other paths which are the paths to our custom folder in the URI and URL form.
We are then checking if that folder exists and if it doesn’t we are creating it. After that we are getting the filename and then we are replacing all the empty places in underscores “_” so a file such as “Picture of me” will be “Picture_of_me”. We are now setting other information about the uploaded file such as the size, error and temporary name.
We are also creating a variable mb which will hold the maximum file size that is allowed to store. After that we are checking if there is an error inside the information about the uploaded file and if there is we are setting the ERROR response with the error message.
If there is no error, we are checking if there is already a file there with the same name and if there is one, we are again setting the ERROR response with the error message that such file already exists.
If there is no file error and the file does not exist already, we are checking for the size of the file. If the file is too big we will set the ERROR response with the error message that the file is too big. But if the file size is under the set size in the variable mb we can start saving the file in our custom folder.
If the file was successfully moved from the temporary folder to our custom folder, we are setting the SUCCESS response with all that required information. If the file was not successfully moved we will again set the ERROR response saying that the file upload failed.
Creating a fancy message for each upload or error
Open our CSS file and at the bottom of it add this few lines:
After that open our JavaScript file and add this function at the top of $(document).ready(function(){});:
This will create a function that will append a new div element with the class .alert and append it to our previously defined element .ibenic_upload_message. After the message is shown, it will fade out slowly. To enable this message to show, we will edit our AJAX success callback function:
Here we have added two calls for the function add_message. One is when the response if SUCCESS and one is when the response is ERROR. We have also removed the alert for the error part since now we have a method to show the error message in a fancy way.
Deleting the Uploaded File
Here we will write the code that will delete the uploaded file and give us back the box to upload a file. First we are going to write some JavaScript code so open our JavaScript file and insert this before the end of $(document).ready(function(){});:
When the user click on the Delete button we will read the file url from the data attribute data-fileurl and add that data with the name of the action. Then we will create an AJAX call on it. In the SUCCESS callback function we will hide the preview box and show again the upload box. If the response is ERROR we will just show the error message given from the server.
We now only need to write the code in PHP to handle the deletion of the file, so open the file where we have already written the code for the upload and add this:
Here we are checking if the global $_POST variable is set. If it is set we are getting the information about the file url and then we are checking in the database if there is an attachment with that url. If that return true we are getting the ID of that attachment and then with the WordPress function wp_delete_attachment we are deleting the file.
If we did not get any information from the database then it must be a custom uploaded file. We are then getting the filename from the url and then we are creating the URI to the file. Here we are referencing the path to our custom folder. If you have named your folder differently then create the URI with your name. After that we can use the PHP function unlink and delete the uploaded file.
Creating a Secure Upload and Delete
For the upload and delete to be more secure we should use the WordPress Nonce. First let’s go to our form and add the nonce:
Now lets add that nonce to our AJAX calls. This will only show you where to add that code:
With that nonce added, the AJAX calls will also send the nonce data to our server. We now need to add a simple check for that nonce. Here you can see where to add them:
With this simple steps we have secured our upload and delete with WordPress Nonces.
Conclusion
WordPress file uploads can be done in many different ways. In this article we have covered only a few but there are also some other WordPress API functions that can upload files in WordPress.
Have you ever done something similar or developed a file uploader for WordPress? Share your experience in the comments below.
www.ibenic.com
Selecting Files to Upload
The first thing you need to do is set up your HTML form that will allow the user to select the file(s) that they wish to upload. To keep things simple lets use a standard <input>
element with the file
type.
<form id="file-form" action="handler.php" method="POST"> <input type="file" id="file-select" name="photos[]" multiple/> <button type="submit" id="upload-button">Upload</button> </form>
Notice that the <input>
element includes the multiple
attribute. This will allow the user to select multiple files from the file picker launched by the browser. If you don’t specify this attribute the user can only select one file.
Now that you’ve got your HTML form set up lets take a look at the JavaScript code that will handle the file upload.
Uploading Files to the Server
First of all you need to create three variables that hold references to the <form>
, <input>
, and <button>
elements in your HTML markup.
var form = document.getElementById('file-form'); var fileSelect = document.getElementById('file-select'); var uploadButton = document.getElementById('upload-button');
Next you need to attach an event listener to the form’s onsubmit
event.
form.onsubmit = function(event) { event.preventDefault(); // Update button text. uploadButton.innerHTML = 'Uploading...'; // The rest of the code will go here... }
Inside the event listener you start by calling preventDefault()
on the event
object passed into the handler. This will prevent the browser from submitting the form, allowing us to handle the file upload using AJAX instead.
Next you update the innerHTML
property on the uploadButton
to Uploading...
. This just provides a bit of feedback to the user so they know the files are uploading.
Your next job is to retrieve the FileList
from the <input>
element and store this in a variable. You can do this by accessing the files
property.
// Get the selected files from the input. var files = fileSelect.files;
You then create a new FormData
object. This is used to construct the key/value pairs which form the data payload for the AJAX request.
// Create a new FormData object. var formData = new FormData();
Your next job is to loop through each of the files in the files
array and add them to the formData
object you just created. You’ll also want to check that the user has selected the type of file you’re expecting.
// Loop through each of the selected files. for (var i = 0; i < files.length; i++) { var file = files[i]; // Check the file type. if (!file.type.match('image.*')) { continue; } // Add the file to the request. formData.append('photos[]', file, file.name); }
Here you’re first fetching the current file from the files
array and then checking to make sure it’s an image. The file’s type
property will return the file type as a string. You can therefore use the JavaScript match()
method to ensure that this string matches the desired type. If the file type does not match, you skip the file by calling continue
.
You then use the append
method on the formData
object to add this file to the data payload.
The FormData.append()
method is used to handle Files, Blobs, or Strings.
// Files formData.append(name, file, filename); // Blobs formData.append(name, blob, filename); // Strings formData.append(name, value);
The first parameter specifies the name
of the data entry. This will form the key
in the data payload. The second parameter specifies either a File
, Blob
, or String
that will be used as the value
for the data entry. When appending a File
or Blob
you can also specify a filename
, but this isn’t required.
Next you need to set up the XMLHttpRequest
that is responsible for communicating with the server. To do this you first need to create a new XMLHttpRequest
object.
// Set up the request. var xhr = new XMLHttpRequest();
You now need to create a new connection to the server. You do this using the open
method. This method takes three parameters. The HTTP method
, the url
that will handle the request, and a boolean value that determines whether the request should be dealt with asynchronously.
// Open the connection. xhr.open('POST', 'handler.php', true);
Next you need to set up an event listener that will be triggered when the onload
event is fired. Examining the status
property of the xhr
object will tell you if the request completed successfully.
// Set up a handler for when the request finishes. xhr.onload = function () { if (xhr.status === 200) { // File(s) uploaded. uploadButton.innerHTML = 'Upload'; } else { alert('An error occurred!'); } };
All that’s left to do now is send the request. Pass the formData
object to the send
method which is available on the xhr
object.
// Send the Data. xhr.send(formData);
That’s everything you need to know to start uploading files using AJAX. Your server-side code will need to extract the files from the request and process them as desired.
Browser Support
Browser support for the technologies used in this post is generally good. Internet Explorer being the only exception. You will be okay with IE 10 and above, but earlier versions of IE didn’t include support for some of the XMLHttpRequest features covered in this post.
IE | Firefox | Chrome | Safari | Opera |
---|---|---|---|---|
10.0+ | 4.0+ | 7.0+ | 5+ | 12+ |
Summary
In this post you’ve learned how to upload files to a web server using native JavaScript technologies. The advancement in functionality of XMLHttpRequests has eliminated the need for developers to use third-party browser plugins to handle file uploads. This is good as native browser features are often faster and more secure than those provided by a plugin.
How do you plan to use AJAX file uploads in your projects? Share your thoughts in the comments below.
blog.teamtreehouse.com
Update
The API I wrote about in this article has been removed from recent versions of Firefox. Instead, you can now use a FileReader
object to read files on the client, and a FormData
object to serialize such file values and POST them using asynchronous requests. These two objects are also available in Chrome and Safari, possible other browsers too. However, I'll keep the old article here for posterity. Here goes.
Old Article
I don't know about you, but there's one little thing I've always hated about Ajax. The impossibility of file uploading. I still remember the ugly day when I discovered the terrible truth. There was no chance on earth you could send a file using an XMLHttpRequest
, thus workarounds appeared. Google made use of a hidden iframe to imitate such an asynchronous call for their Gmail service, and later on, Flash based uploaders appeared. Things are though going forward.
Firefox 3 introduced a new API, that few people know about, which allows JavaScript to read local files selected by an user from a form file-upload dialog. A copy of Firefox 3 with default settings offers full access to this new programming interface. So we'll embrace the challenge and write a little JavaScript uploader on top of this new API.
However, if you're not into reading this long article, just go ahead and look at the complete source code in my GitHub repo.
Goal
In this tutorial we'll write a little application that is able to read and upload local files to a remote web server using an asynchronous HTTP request. The whole application consists of three parts:
- the client side comprised of JavaScript, HTML and a little CSS
- the server side script, written in PHP
- the communication channel in between them, old good HTTP
We'll start with the JavaScript side, as I'm sure you're eager to find about the new file access API, then we'll review the XMLHttpRequest
object as available in Firefox version 3 (it got enhanced with a new method). After the client-side part follows explanations for the server-side script, written in PHP, continued with a little summary over the HTTP protocol concerning data transmission and HTML forms. Finally, we'll put all these together to build a powerful asynchronous file uploader.
The Firefox file upload API
When Firefox 3 has been launched in June this year, we heard a lot about the improvements it brought to the web development field by further implementing existing standards and technologies like HTML, CSS and JavaScript. One thing I have never seen mentioned was the interface for reading local files, provided the file is chosen by the user through an HTML file input element. For example, a simple Google search for getAsBinary, one of the new methods in the API, will give you few results, even when counting the false positives (such a false positive is related to ColdFusion which has a similar method name, and results comprising information about it are preponderant). That surprises me a lot as, in my opinion, it is a huge step forward in building more powerful web applications. Actually, there is someone that wrote about it in May 2008. Alas the news hasn't spread. With this new API, each input element (not only file input elements), is given a property called files. This property is our gateway to reading local files. When the type attribute of the input element isn't file, the value of the files property is null. On the other hand, for input elements whose type attribute is file, the files property is of type FileList
and resembles a NodeList
object returned by, let's say, document.getElementsByTagName()
. You may access it as if it were an Array
and has the following properties and methods:
length
item(index)
Each element in the files property is a File element that exposes the following properties and methods:
fileName
fileSize
getAsBinary()
getAsText(encoding)
getAsDataURL()
Those two lists above are all there is to know about the API for reading local files. There is nothing more about it. No security restrictions, no special configurations. As I'm sure the files property itself poses no problem in understanding its interface let's review the contained file objects with a little script. You may want to download Firebug in order to get a thorough understanding of the following exploration. Here's the code that we'll use:
The fileName
property gives us the name of the picked up file, but not the absolute path on the filesystem, just the basename. Modify the above source code above so that the JavaScript part now becomes:
The getAsBinary()
method will read the contents of the file and return them in binary representation. If you select a binary file, an image for example, you should see some weird characters, question marks or even rectangles in the alert, this is how Firefox represents the bytes contained in the file. For a text file it will simply output its text.
The getAsText(encoding)
method will return the contents as a string of bytes encoded depending on the encoding parameter. This is by default UTF-8, but the encoding parameter it's not really optional. You still have to pass some value. An empty string will do it just fine:
Finally, the getAsDataURL()
method, a very interesting and very useful one, will return the file contents in a format ideally suited for, let's say, the src attribute of an IMG
tag. Of course, this will work as we're in Firefox right now, so let's add a IMG
tag to the HTML source and the appropriate JavaScript code to make this work:
More information about the new API can be found on their dedicated pages on Mozilla Developer Center:
- https://developer.mozilla.org/en/nsIDOMFileList
- https://developer.mozilla.org/en/nsIDOMFile
Some extra information
You may wonder why an array of files for just one input element. It turns out that RFC 1867, concerning form-based file uploads, specifies that a file input element allows its size attribute to receive a complex value:
The
SIZE
attribute might be specified usingSIZE=width,height
, where width is some default for file name width, while height is the expected size showing the list of selected files. For example, this would be useful for forms designers who expect to get several files and who would like to show a multiline file input field in the browser (with a "browse" button beside it, hopefully). It would be useful to show a one line text field when no height is specified (when the forms designer expects one file, only) and to show a multiline text area with scrollbars when the height is greater than 1 (when the forms designer expects multiple files).
None of the browsers I tested this in seems to obey the RFC, nevertheless this should be the reason for which the files property is an array-like object.
The XMLHttpRequest object
Now that we're able to read local files, we need a way to get this data, over the network, to the server. As we're aiming for an asynchronous data transmission, an XMLHttpRequest
object should do the job just fine. Unfortunately, its send()
method isn't that reliable in sending binary data. For this reason, along with the local file access interface, Firefox 3 brought a new method to the XMLHttpRequest
object: sendAsBinary(data)
. Just as the send()
method, the new one takes a single argument, a string, which is converted to a string of single-byte characters by truncation (removing the high-order byte of each character), according to the documentation. The difference, a very important one, is that, as long as send()
knows how to process an URL query string, sendAsBinary()
expects a totally different format in order to be useful for the server-side end of the application, but we'll talk about this a little bit later. Let's just write a little JavaScript skeleton, that we'll use when assembling together all the pieces of the application:
As you see, there's an undefined variable in the above snippet, data, which remains to be defined after we review the mechanism behind files upload over HTTP. For the moment though, I want to talk about the server-side part, as it will guide us in choosing the appropriate strategy for sending binary information.
The server-side script
PHP, as well as several web frameworks made on top of other languages, offers different access points for reading POST data containing uploaded files and POST data containing just simple values. In PHP there are two predefined arrays giving you access to simple POST data, the $_POST
array, and another one for accessing files sent to the server, the $_FILES
array. It is thus wise to build a client-side script able to send information that PHP would read as from a classical request issued with an HTML form: our uploaded files would appear as elements inside the $_FILES
array and additional values as elements inside the $_POST
array. Under these circumstances we can write the PHP script to test that our JavaScript client is performing well:
That's all we need on the server. Although simple this we'll give us valuable feedback about the sent data. The PHP script should list our uploaded files inside the $_FILES
array and any additional form data (like text INPUT
or SELECT
element) inside the $_POST
array.
Form data over HTTP theory
As we saw, PHP treats POST-ed files differently than ordinary form field values, so it's only natural to ask ourselves what's the "clue" that helps PHP tell apart one from the other.
First of all, every HTML form element out there has an optional attribute called enctype, with a default value of application/x-www-form-urlencoded
. This is actually a MIME type value specifying the encoding to be used by the web browser when sending form data. It also guides the web server script in decoding that data as the encoding is sent by the browser to the server in the form of an HTTP header, called Content-Type
. For a default enctype value, form data is sent as ASCII characters, URL encoded when necessary. On the other hand, when uploading files, we need to change the enconding to multipart/form-data
. Cloning this basic form functionality inside our JavaScript client is what we should do. Let's modify the earlier script so that it sends such a multipart/form-data
header:
RFC 1867, regarding form-based file uploads, dictates that an extra parameter is required for the `Content-Type` header when its value is `multipart/form-data`. It is called boundary and its presence it’s very logical. The multipart word inside the header means the sent request is formed of multiple parts (obviously), so there must be something to separate those parts. This thing is the boundary parameter which value must be a string of characters that shouldn’t be found inside any of the form values we send, otherwise the request will get garbled. Once again, let’s modify the script to reflect this requirement. ~~~js var xhr = new XMLHttpRequest; xhr.onreadystatechange = function() { if (xhr.readyState === 4) { alert(xhr.responseText); } }; /* * The value of the boundary doesn’t matter as long as no other structure in * the request contains such a sequence of characters. We chose, nevertheless, * a pseudo-random value based on the current timestamp of the browser. */ var boundary = «AJAX—————» + (new Date).getTime(); var contentType = «multipart/form-data; boundary=» + boundary; xhr.setRequestHeader(«Content-Type», contentType); xhr.sendAsBinary(data); ~~~ Next, let’s talk about the structure of the parts comprised in the request. Each of these is like a little request on its own. Each has its own headers structure and body. The mandatory header, every part must have, is called `Content-Disposition` and its value should be form-data followed by an additional parameter called name, which represents the name of the form input that holds the data. In case of parts holding uploaded files, a second parameter must also be present, called filename. This is the name of the file as it was on the user’s computer. Not the absolute path, just the basename (for example, monkey.png). Parameter values are enclosed inside double quotes. A little example: Content-Disposition: form-data; name=»photo»; filename=»monkey.png» In the case of files, a second header must is also needed. It is called `Content-Type` and specifies the MIME
type of the file. This may be deduced by reading the file extension or the source of the file. Anyway, a general value of application/octet-stream
is perfectly acceptable:
As per the standards, every such header should end with two characters, a carriage return and a new line: CR
and LF
. The last header doubles this sequence (i.e. it ends with CRLFCRLF
).
Following the headers is the body which consists of the form field value. I'll illustrate with a simple text file upload, although it could be any type of file. The text inside the file is "my random notes about web programming":
Finally I'll give you a final example with both the HTML markup of the form as well as a fictional request from that form:
The issued request could be:
In case you didn't noticed, the boundary, when used in between the parts is prepended with two hyphens and the last one appended with also two hyphens. Don't forget about this, it's an ugly source of bugs.
Encapsulating the JavaScript logic
At this point we know all the parts to successfully build a pure JavaScript file uploader, which we're going to implement as an object. Here's the basic structure of the constructor and its prototype:
In case you didn't understand the elements()
construct, this is called a getter and is supported by the latest versions of Firefox, Opera, Safari and Chrome. A setter form is also provided. You can find more about these on Mozilla Developer Center.
We should fill the above methods, so let's start with the send()
method because we already wrote much of it in a previous sections of the tutorial:
In addition to what we had earlier we have now introduced an iteration that allows us to send additional headers without modifying the prototype itself. We have also defined the variable we talked about earlier, data, which holds the source of the HTTP request. It holds the value returned by a call to a method whose only purpose is to build an HTTP compliant request for file uploads. Here's its source:
Although it looks complex, it has a fair amount of comments so that you won't have hard times understanding what it does. It simply iterates over an array of HTML elements and for each such an element constructs a different message depending whether the element is a file upload input or not. It pushes this message into an internal array, which is finally joined using the boundary sent as an argument inside the send()
method.
Here follows the source of the elements()
getter, used in send()
:
The snippet above grabs all the INPUT
and SELECT
elements inside the FORM
element associated with the Uploader
object. These elements are eventually returned into a unified array. There are, however no checks on these elements, like filtering disabled controls. Furthermore, the RFC specifies that a client should send form data in the same order it was rendered in the user agent. For keeping the examples as short as I could, the method above doesn't take care of that, but the code inside the accompanying archive does.
The final piece of code left for presentation is the generateBoundary()
method which must return a string unique in the body of our request. For our simple example though, the method below should be just fine:
The code inside is building a string based on the current timestamp to which some other characters are prepended. I'm using the uppercased word "AJAX" and some dashes, but this prefix isn't mandatory, the only condition that must be met is that the result of generateBoudary()
should not appear anywhere else in out request except for the boundary placeholders.
Finally, the headers property of our object remains like it was in the skeleton. It is there so that you can append additional headers to the request, for example:
Save the source of the Uploader object in a file called "uploader.js", we'll use it in a few moments.
Putting it all together
Let's now write the final HTML source and save it inside a file called "index.html". Aside the markup, the code below introduces some event listeners for the "Upload" and "Preview" button:
Now create another file, save it as upload.php, and write inside it the code we presented in the server-side section of the tutorial:
That's all. You may now test the application, which will hopefully work from the first run. Don't forget to install Firebug to inspect what's happening behind the scenes.
igstan.ro
I will show you how to upload a file using PHP, AJAX and jQuery without page refresh. This file upload tutorial example is very helpful to implement the upload functionality. In this example the file is selected using the browse button and file is uploaded to the ‘uploads’ directory. PHP script stores the file into the defined location and returns response as a success or failure message from the PHP script. As a validation step I have added only to check if you have selected a file for uploading or not.
You may read also AJAX Multiple Files Upload using PHP, jQuery and AJAX File Upload using Codeigniter, jQuery
Following steps are required in order to upload a file
1. Include jQuery library.
2. HTML page with upload field.
3. jQuery Ajax code.
3. PHP script to store the file.
Step 1. Include jQuery library into <head/> section of the HTML page
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
Step 2. Put the below upload field inside <body/> tag in HTML page
<p id="msg"></p> <input type="file" id="file" name="file" /> <button id="upload">Upload</button>
Step 3. Add below jQuery, AJAX code immediate after the jQuery library inside <head/> section of the HTML page
<script type="text/javascript"> $(document).ready(function (e) { $('#upload').on('click', function () { var file_data = $('#file').prop('files')[0]; var form_data = new FormData(); form_data.append('file', file_data); $.ajax({ url: 'upload.php', // point to server-side PHP script dataType: 'text', // what to expect back from the PHP script cache: false, contentType: false, processData: false, data: form_data, type: 'post', success: function (response) { $('#msg').html(response); // display success response from the PHP script }, error: function (response) { $('#msg').html(response); // display error response from the PHP script } }); }); }); </script>
Step 4. Create upload.php file with below source code
<?php if (isset($_FILES['file']['name'])) { if (0 < $_FILES['file']['error']) { echo 'Error during file upload' . $_FILES['file']['error']; } else { if (file_exists('uploads/' . $_FILES['file']['name'])) { echo 'File already exists : uploads/' . $_FILES['file']['name']; } else { move_uploaded_file($_FILES['file']['tmp_name'], 'uploads/' . $_FILES['file']['name']); echo 'File successfully uploaded : uploads/' . $_FILES['file']['name']; } } } else { echo 'Please choose a file'; } /* * End of script */
If we combine the steps 1, 2, 3 and 4 then the full source code would be as shown below in a file called file_upload_ajax.php
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>File upload using PHP, jQuery and AJAX</title> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script> <script type="text/javascript"> $(document).ready(function (e) { $('#upload').on('click', function () { var file_data = $('#file').prop('files')[0]; var form_data = new FormData(); form_data.append('file', file_data); $.ajax({ url: 'upload.php', // point to server-side PHP script dataType: 'text', // what to expect back from the PHP script cache: false, contentType: false, processData: false, data: form_data, type: 'post', success: function (response) { $('#msg').html(response); // display success response from the PHP script }, error: function (response) { $('#msg').html(response); // display error response from the PHP script } }); }); }); </script> </head> <body> <p id="msg"></p> <input type="file" id="file" name="file" /> <button id="upload">Upload</button> </body> </html>
Now if you run the above file then you will see below output in the browser
If you try to upload file without selecting any file then you would see error message “Please choose a file”.
If the file successfully uploaded then you will see success message “File successfully uploaded : uploads/<some-file-name>“.
If the file you are trying to upload already exists then you will see a message “File already exists : uploads/<some-file-name>“.
Hope you have got an idea how to upload file using PHP, AJAX and jQuery.
Thanks for reading.
Tags: AJAX file upload • file upload • Jquery File Upload • PHP AJAX • PHp File upload • PHP jQuery
www.roytuts.com
I have implemented a multiple file select with instant preview and upload after removing unwanted files from preview via ajax.
Detailed documentation can be found here: http://anasthecoder.blogspot.ae/2014/12/multi-file-select-preview-without.html
Demo: http://jsfiddle.net/anas/6v8Kz/7/embedded/result/
jsFiddle: http://jsfiddle.net/anas/6v8Kz/7/
Javascript:
$(document).ready(function(){ $('form').submit(function(ev){ $('.overlay').show(); $(window).scrollTop(0); return upload_images_selected(ev, ev.target); }) }) function add_new_file_uploader(addBtn) { var currentRow = $(addBtn).parent().parent(); var newRow = $(currentRow).clone(); $(newRow).find('.previewImage, .imagePreviewTable').hide(); $(newRow).find('.removeButton').show(); $(newRow).find('table.imagePreviewTable').find('tr').remove(); $(newRow).find('input.multipleImageFileInput').val(''); $(addBtn).parent().parent().parent().append(newRow); } function remove_file_uploader(removeBtn) { $(removeBtn).parent().parent().remove(); } function show_image_preview(file_selector) { //files selected using current file selector var files = file_selector.files; //Container of image previews var imageContainer = $(file_selector).next('table.imagePreviewTable'); //Number of images selected var number_of_images = files.length; //Build image preview row var imagePreviewRow = $('<tr class="imagePreviewRow_0"><td valign=top style="width: 510px;"></td>' + '<td valign=top><input type="button" value="X" title="Remove Image" class="removeImageButton" imageIndex="0" onclick="remove_selected_image(this)" /></td>' + '</tr> '); //Add image preview row $(imageContainer).html(imagePreviewRow); if (number_of_images > 1) { for (var i =1; i<number_of_images; i++) { /** *Generate class name of the respective image container appending index of selected images, *sothat we can match images selected and the one which is previewed */ var newImagePreviewRow = $(imagePreviewRow).clone().removeClass('imagePreviewRow_0').addClass('imagePreviewRow_'+i); $(newImagePreviewRow).find('input[type="button"]').attr('imageIndex', i); $(imageContainer).append(newImagePreviewRow); } } for (var i = 0; i < files.length; i++) { var file = files[i]; /** * Allow only images */ var imageType = /image.*/; if (!file.type.match(imageType)) { continue; } /** * Create an image dom object dynamically */ var img = document.createElement("img"); /** * Get preview area of the image */ var preview = $(imageContainer).find('tr.imagePreviewRow_'+i).find('td:first'); /** * Append preview of selected image to the corresponding container */ preview.append(img); /** * Set style of appended preview(Can be done via css also) */ preview.find('img').addClass('previewImage').css({'max-width': '500px', 'max-height': '500px'}); /** * Initialize file reader */ var reader = new FileReader(); /** * Onload event of file reader assign target image to the preview */ reader.onload = (function(aImg) { return function(e) { aImg.src = e.target.result; }; })(img); /** * Initiate read */ reader.readAsDataURL(file); } /** * Show preview */ $(imageContainer).show(); } function remove_selected_image(close_button) { /** * Remove this image from preview */ var imageIndex = $(close_button).attr('imageindex'); $(close_button).parents('.imagePreviewRow_' + imageIndex).remove(); } function upload_images_selected(event, formObj) { event.preventDefault(); //Get number of images var imageCount = $('.previewImage').length; //Get all multi select inputs var fileInputs = document.querySelectorAll('.multipleImageFileInput'); //Url where the image is to be uploaded var url= "/upload-directory/"; //Get number of inputs var number_of_inputs = $(fileInputs).length; var inputCount = 0; //Iterate through each file selector input $(fileInputs).each(function(index, input){ fileList = input.files; // Create a new FormData object. var formData = new FormData(); //Extra parameters can be added to the form data object formData.append('bulk_upload', '1'); formData.append('username', $('input[name="username"]').val()); //Iterate throug each images selected by each file selector and find if the image is present in the preview for (var i = 0; i < fileList.length; i++) { if ($(input).next('.imagePreviewTable').find('.imagePreviewRow_'+i).length != 0) { var file = fileList[i]; // Check the file type. if (!file.type.match('image.*')) { continue; } // Add the file to the request. formData.append('image_uploader_multiple[' +(inputCount++)+ ']', file, file.name); } } // Set up the request. var xhr = new XMLHttpRequest(); xhr.open('POST', url, true); xhr.onload = function () { if (xhr.status === 200) { var jsonResponse = JSON.parse(xhr.responseText); if (jsonResponse.status == 1) { $(jsonResponse.file_info).each(function(){ //Iterate through response and find data corresponding to each file uploaded var uploaded_file_name = this.original; var saved_file_name = this.target; var file_name_input = '<input type="hidden" class="image_name" name="image_names[]" value="' +saved_file_name+ '" />'; file_info_container.append(file_name_input); imageCount--; }) //Decrement count of inputs to find all images selected by all multi select are uploaded number_of_inputs--; if(number_of_inputs == 0) { //All images selected by each file selector is uploaded //Do necessary acteion post upload $('.overlay').hide(); } } else { if (typeof jsonResponse.error_field_name != 'undefined') { //Do appropriate error action } else { alert(jsonResponse.message); } $('.overlay').hide(); event.preventDefault(); return false; } } else { /*alert('Something went wrong!');*/ $('.overlay').hide(); event.preventDefault(); } }; xhr.send(formData); }) return false; }
stackoverflow.com
Ajax File Upload with PHP, HTML5 File API and jQuery
Ajax file uploader is useful web tool, which is actually very easy to create with jQuery and PHP, and with new HTML5 FileReader API support, we can do client side file validation before even uploading the file to server. Today we are going to combine all these web features to make an Ajax based file uploader, which can be easily integrated into any HTML forms.
Upload Form Markup
HTML contains file input field, upload button, progress bar and DIV element to output server responses. Only thing to notice here is «multipart/form-data» attribute, it is a data encoding method available in HTML and we always need to include this attribute in our HTML form tag, when creating file upload forms.
jQuery and HTML5 File API
Since we are using jQuery, I’ve included jQuery form plugin in our upload form to make things easier. It offers so many options to manipulate form with minimal code, which is great thing, because we can focus on other things like HTML5 File API. The FileReader is a powerful new feature of HTML5, which lets us read the contents of files locally, validating file is easier than ever. Earlier people used different methods to achieve the same, but it was a daunting task. We only need these few lines of jQuery code to actually send our file to server using Ajax, with jQuery Form plugin included in our page. No more code is required, but as you can see it also accepts various options such as «beforeSubmit«, «success«, «uploadProgress» etc. (You can find more options here). which can greatly extend the functionality of our form, we can just create block of functions and call them within the option.
Checking HTML5 File API Browser support
Let’s write a beforeSubmit function, which will be called before uploading the file to server, and it is our important piece of code here, because we are going to use HTML File API in it, which will validate the file size and type locally. Since more and more browsers now support HTML FileReader, I think we should definitely use it more often in our upload forms.
Checking File size and type before upload using HTML5 File API
The following code accesses the browser File API support and checks whether the user is trying to upload right file type and its size is under the allowed limit. Older browsers will get an error message asking to upgrade their browser.
Progress Bar
It’s always a good idea to include a progress bar in our file uploader to track upload progress, since we are dealing with different files and unlike images, other files can be bigger in size and can take quite a long time to upload. The jQuery form plugin passes several progress information to our function (If supported by browser), which we will capture and use to animate our progress bar.
Server side PHP
Once the file is successfully uploaded to server via the HTTP POST method, we can access the file information using PHP code. The information we need here are file name, type and size, using the information here too we double check for valid file size and type. And then using PHP move_uploaded_file function, we will move the file to a new location, then we can just add mySQL code to store information in the database. Here’s the complete PHP code which does the server side task, you need to place this file along with upload HTML form page we created earlier, just point your Ajax form to this file. That’s it, I have tried to make this tutorial very simple, you can download the sample files from below and continue with testing. The demo you’ll see is from different tutorial, but works similar way and it only allows image files. If you have any suggestions please go ahead and leave your valuable comments, good luck! Download Demo
www.sanwebe.com