Can someone explain how to implement the jQuery File Upload plugin?
EDIT (Oct 2019):
6 years later and jQuery File Upload is clearly still driving folks insane. If you're finding little solace in the answers here, try a search of NPM for a modern alternative. It's not worth the hassle, I promise.
I recommended Uploadify in the previous edit but, as a commenter pointed out, they no longer appear to offer a free version. Uploadify was so 2013 anyway.
EDIT:
This still seems to be getting traffic so I'll explain what I ended up doing. I eventually got the plugin working by following the tutorial in the accepted answer.Background
I'm trying to use blueimp's jQuery File Upload to allow users to upload files. Out of the box it works perfectly, following the setup instructions. But to use it practically on my website, I want to be able to do a couple of things:
- Include the uploader on any of my existing pages
- Change the directory for uploaded files
All the files for the plugin are located in a folder under the root.
I've tried...
- Moving the demo page into the root and updating the paths for the necessary scripts
- Changing the 'upload_dir' and 'upload_url' options in the UploadHandler.php file as suggested here.
- Changing the URL in the second line of the demo javascript
In all cases, the preview shows, and the progress bar runs, but the files fail to upload, and I get this error in the console: Uncaught TypeError: Cannot read property 'files' of undefined
. I don't understand how all the parts of the plugin work which is making it difficult to debug.
Code
The javascript in the demo page:
$(function () {
'use strict';
// Change this to the location of your server-side upload handler:
var url = 'file_upload/server/php/UploadHandler.php',
uploadButton = $('<button/>')
.addClass('btn')
.prop('disabled', true)
.text('Processing...')
.on('click', function () {
var $this = $(this),
data = $this.data();
$this
.off('click')
.text('Abort')
.on('click', function () {
$this.remove();
data.abort();
});
data.submit().always(function () {
$this.remove();
});
});
$('#fileupload').fileupload({
url: url,
dataType: 'json',
autoUpload: false,
acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
maxFileSize: 5000000, // 5 MB
// Enable image resizing, except for Android and Opera,
// which actually support image resizing, but fail to
// send Blob objects via XHR requests:
disableImageResize: /Android(?!.*Chrome)|Opera/
.test(window.navigator.userAgent),
previewMaxWidth: 100,
previewMaxHeight: 100,
previewCrop: true
}).on('fileuploadadd', function (e, data) {
data.context = $('<div/>').appendTo('#files');
$.each(data.files, function (index, file) {
var node = $('<p/>')
.append($('<span/>').text(file.name));
if (!index) {
node
.append('<br>')
.append(uploadButton.clone(true).data(data));
}
node.appendTo(data.context);
});
}).on('fileuploadprocessalways', function (e, data) {
var index = data.index,
file = data.files[index],
node = $(data.context.children()[index]);
if (file.preview) {
node
.prepend('<br>')
.prepend(file.preview);
}
if (file.error) {
node
.append('<br>')
.append(file.error);
}
if (index + 1 === data.files.length) {
data.context.find('button')
.text('Upload')
.prop('disabled', !!data.files.error);
}
}).on('fileuploadprogressall', function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
$('#progress .bar').css(
'width',
progress + '%'
);
}).on('fileuploaddone', function (e, data) {
$.each(data.result.files, function (index, file) {
var link = $('<a>')
.attr('target', '_blank')
.prop('href', file.url);
$(data.context.children()[index])
.wrap(link);
});
}).on('fileuploadfail', function (e, data) {
$.each(data.result.files, function (index, file) {
var error = $('<span/>').text(file.error);
$(data.context.children()[index])
.append('<br>')
.append(error);
});
}).prop('disabled', !$.support.fileInput)
.parent().addClass($.support.fileInput ? undefined : 'disabled');
});
I'm surprised by the lack of documentation; it seems like it should be a simple thing to change. I would appreciate if someone could explain how to do this.
Solution 1:
I was looking for a similar functionality some days back and came across a good tutorial on tutorialzine. Here is an working example. Complete tutorial can be found here.
Simple form to hold the file upload dialogue:
<form id="upload" method="post" action="upload.php" enctype="multipart/form-data">
<input type="file" name="uploadctl" multiple />
<ul id="fileList">
<!-- The file list will be shown here -->
</ul>
</form>
And here is the jQuery code to upload the files:
$('#upload').fileupload({
// This function is called when a file is added to the queue
add: function (e, data) {
//This area will contain file list and progress information.
var tpl = $('<li class="working">'+
'<input type="text" value="0" data-width="48" data-height="48" data-fgColor="#0788a5" data-readOnly="1" data-bgColor="#3e4043" />'+
'<p></p><span></span></li>' );
// Append the file name and file size
tpl.find('p').text(data.files[0].name)
.append('<i>' + formatFileSize(data.files[0].size) + '</i>');
// Add the HTML to the UL element
data.context = tpl.appendTo(ul);
// Initialize the knob plugin. This part can be ignored, if you are showing progress in some other way.
tpl.find('input').knob();
// Listen for clicks on the cancel icon
tpl.find('span').click(function(){
if(tpl.hasClass('working')){
jqXHR.abort();
}
tpl.fadeOut(function(){
tpl.remove();
});
});
// Automatically upload the file once it is added to the queue
var jqXHR = data.submit();
},
progress: function(e, data){
// Calculate the completion percentage of the upload
var progress = parseInt(data.loaded / data.total * 100, 10);
// Update the hidden input field and trigger a change
// so that the jQuery knob plugin knows to update the dial
data.context.find('input').val(progress).change();
if(progress == 100){
data.context.removeClass('working');
}
}
});
//Helper function for calculation of progress
function formatFileSize(bytes) {
if (typeof bytes !== 'number') {
return '';
}
if (bytes >= 1000000000) {
return (bytes / 1000000000).toFixed(2) + ' GB';
}
if (bytes >= 1000000) {
return (bytes / 1000000).toFixed(2) + ' MB';
}
return (bytes / 1000).toFixed(2) + ' KB';
}
And here is the PHP code sample to process the data:
if($_POST) {
$allowed = array('jpg', 'jpeg');
if(isset($_FILES['uploadctl']) && $_FILES['uploadctl']['error'] == 0){
$extension = pathinfo($_FILES['uploadctl']['name'], PATHINFO_EXTENSION);
if(!in_array(strtolower($extension), $allowed)){
echo '{"status":"error"}';
exit;
}
if(move_uploaded_file($_FILES['uploadctl']['tmp_name'], "/yourpath/." . $extension)){
echo '{"status":"success"}';
exit;
}
echo '{"status":"error"}';
}
exit();
}
The above code can be added to any existing form. This program automatically uploads images, once they are added. This functionality can be changed and you can submit the image, while you are submitting your existing form.
Updated my answer with actual code. All credits to original author of the code.
Source: http://tutorialzine.com/2013/05/mini-ajax-file-upload-form/
Solution 2:
I've just spent 2 hours battling with jQuery Upload but gave up because of the amount of dependencies (I had 13 JS files included to get all the bells and whistles).
I did a bit more searching and came across a neat project called Dropzone.js, which does not have any dependencies.
The author has also created a bootstrap demo which was inspired by the jQuery File Upload plugin.
I hope this saves someone else some time.
Solution 3:
I also struggled with this but got it working once I figured out how the paths work in UploadHandler.php: upload_dir and upload_url are about the only settings to look at to get it working. Also check your server error logs for debugging information.