I am using Jquery Form Plugin to upload files using onchange event listener on the file upload input element to upload the file as soon as it is selected.
I want to allow only one file at a time to be uploaded and adding the rest in a queue to be uploaded once the existing upload is complete.
I tried making this happen by using a variable and setting it false in beforeSubmit and then switching it back to true once the upload is complete. However, it keeps turning true automatically.
Here is my Javascript code:
var allowUpload = true;
console.log('initial');
var options = {
beforeSubmit: beforeSubmit,
uploadProgress: OnProgress,
success: afterSuccess,
resetForm: true
};
$('#upload').change(function() {
console.log(allowUpload);
if(allowUpload)
{
console.log('onchange:' + allowUpload);
$('#uploadForm').ajaxSubmit(options);
}
return false;
});
function afterSuccess(data)
{
allowUpload = true;
console.log('aftersuccess' + allowUpload);
}
function beforeSubmit(data)
{
allowUpload = false;
console.log(allowUpload);
}
Related
I would like to have a simple drop zone to upload image via AJAX and jQuery. I have found some plugins but they are way too customized for what's needed, and I cannot get any of them working properly.
I also would like the drop zone to be clickable, in order to manually choose a file from the OS file dialog.
I found this script, that works fine but where the drop zone is not clickable:
// ---------------------------- drop zone to upload image : '#dropfile'
$(document).on('dragenter', '#dropfile', function() {
return false;
});
$(document).on('dragover', '#dropfile', function(e){
e.preventDefault();
e.stopPropagation();
return false;
});
$(document).on('dragleave', '#dropfile', function(e) {
e.preventDefault();
e.stopPropagation();
return false;
});
$(document).on('drop', '#dropfile', function(e) {
if(e.originalEvent.dataTransfer){
if(e.originalEvent.dataTransfer.files.length) {
// Stop the propagation of the event
e.preventDefault();
e.stopPropagation();
// Main function to upload
upload(e.originalEvent.dataTransfer.files);
}
}
return false;
});
function upload(files) {
var f = files[0] ;
// Only process image files.
if (!f.type.match('image/jpeg')) {
alert(‘The file must be a jpeg image’) ;
return false ;
}
var reader = new FileReader();
// When the image is loaded, run handleReaderLoad function
reader.onload = handleReaderLoad;
// Read in the image file as a data URL.
reader.readAsDataURL(f);
}
function handleReaderLoad(evt) {
var pic = {};
pic.file = evt.target.result.split(',')[1];
var str = jQuery.param(pic);
$.ajax({
type: 'POST',
url: ‘url_to_php_script.php’,
data: str,
success: function(data) {
//do_something(data) ;
}
});
}
So I added an invisible file type input, but image data seems to be sent twice. I suppose it's due a bad event propagation with the original drop zone:
// ---------------------------- clickable drop zone with invisible file input '#inputFile'
$('#dropfile).on('click', function() {
$('input#inputFile').trigger('click');
$('input#inputFile').change(function(e) {
upload($('input#inputFile')[0].files);
});
});
I tried to add these lines but data is always sent twice:
$('#dropfile).on('click', function() {
$('input#inputFile').trigger('click');
$('input#inputFile').change(function(e) {
upload($('input#inputFile')[0].files);
// -------------- stop sending data twice ???
e.preventDefault();
e.stopPropagation();
return false;
});
});
I still don't know why data is sent twice but I found a better script here:
https://makitweb.com/drag-and-drop-file-upload-with-jquery-and-ajax/
I have decided to update this question as I still can't change the imageId
This is my current dropzone implementation
$("div#UploadImage").dropzone({
url: '#Url.Action("SaveUploadedPhoto", "Listing", new { advertId = #Model.AdvertId })',
addRemoveLinks: true,
init: function () {
var myDropzone = this;
// First change the button to actually tell Dropzone to process the queue.
$("input[type=submit]").on("click", function (e) {
// Make sure that the form isn't actually being sent.
e.preventDefault();
e.stopPropagation();
myDropzone.processQueue();
});
myDropzone.on("success", function (file, rep) {
console.log(rep.UniqueId);
fileList[i] = { "serverFileName": rep.UniqueId, "fileName": rep.UniqueId, "fileId": rep.UniqueId, "name": rep.UniqueId };
i++;
console.log(fileList);
});
myDropzone.on("removedfile", function (file) {
var rmvFile = "";
for (f = 0; f < fileList.length; f++) {
if (fileList[f].fileName == file.name) {
rmvFile = fileList[f].serverFileName;
}
}
if (rmvFile) {
$.ajax({
url: '#Url.Action("DeleteUploadedFile", "Listing")',
type: "POST",
data: { "id": rmvFile }
});
}
});
},
maxFilesize: 2,
maxFiles: 12
});
When I upload an image, we pass the image to a third party company which returns a uniqueId for that image, this uniqueId then gets saved in my database, I then pass this uniqueId back to the view which will become the uploaded image name, I'm trying to do this in the "success" function in the above implementation of dropzone, when I do
console.log(rep.UniqueId);
I can see the value and its correct.
When I press "Remove image" on dropzone the "removedFile" function is called, this doesn't fire to the controller because rmvFile is the old image name and it doesn't find a match within the for loop, I know this because when I do
console.log(rmvFile);
It shows the old name, now my question is what am I doing wrong here? how can I change the uploaded image name or id to the uniqueId which is returned after the uploaded has been completed? so I can successfully delete it as and when it required.
I have browsed the web, tried situation's which has resulted in how my success method currently looks.
I've managed to get this working now, after trying many different solutions.
I have found doing the following passes in the uniqueId for the image
file.serverId['UniqueId'],
I'm wondering if there's any way to make Dropzone.js (http://dropzonejs.com) work with a standard browser POST instead of AJAX.
Some way to inject the inputs type=file in the DOM right before submit maybe?
No. You cannot manually set the value of a <input type='file'> for security reasons. When you use Javascript drag and drop features you're surpassing the file input altogether. Once a file is fetched from the user's computer the only way to submit the file to the server is via AJAX.
Workarounds: You could instead serialize the file or otherwise stringify it and append it to the form as a string, and then unserialize it on the server side.
var base64Image;
var reader = new FileReader();
reader.addEventListener("load", function () {
base64Image = reader.result;
// append the base64 encoded image to a form and submit
}, false);
reader.readAsDataURL(file);
Perhaps you're using dropzone.js because file inputs are ugly and hard to style? If that is the case, this Dropzone.js alternative may work for you. It allows you to create custom styled inputs that can be submitted with a form. It supports drag and drop too, but with drag and drop you cannot submit the form the way you want. Disclaimer: I am author of aforementioned library.
So, if I understood correctly you want to append some data (input=file) before submit your form which has dropzone activated, right?
If so, I had to do almost the same thing and I got it through listening events. If you just upload one file, you should listen to "sending" event, but if you want to enable multiple uploads you should listen to "sendingmultiple". Here is a piece of my code that I used to make this work:
Dropzone.options.myAwesomeForm = {
acceptedFiles: "image/*",
autoProcessQueue: false,
uploadMultiple: true,
parallelUploads: 100,
maxFiles: 100,
init: function() {
var myDropzone = this;
[..some code..]
this.on("sendingmultiple", function(files, xhr, formData) {
var attaches = $("input[type=file]").filter(function (){
return this.files.length > 0;
});
var numAttaches = attaches.length;
if( numAttaches > 0 ) {
for(var i = 0; i < numAttaches; i++){
formData.append(attaches[i].name, attaches[i].files[0]);
$(attaches[i]).remove();
}
}
});
[..some more code..]
}
}
And that's it. I hope you find it helpful :)
PS: Sorry if there's any grammar mistakes but English is not my native language
For future visitors
I've added this to dropzone options:
addedfile: function (file) {
var _this = this,
attachmentsInputContainer = $('#attachment_images');
file.previewElement = Dropzone.createElement(this.options.previewTemplate);
file.previewTemplate = file.previewElement;
this.previewsContainer.appendChild(file.previewElement);
file.previewElement.querySelector("[data-dz-name]").textContent = file.name;
file.previewElement.querySelector("[data-dz-size]").innerHTML = this.filesize(file.size);
if (this.options.addRemoveLinks) {
file._removeLink = Dropzone.createElement("<a class=\"dz-remove\" href=\"javascript:undefined;\">" + this.options.dictRemoveFile + "</a>");
file._removeLink.addEventListener("click", function (e) {
e.preventDefault();
e.stopPropagation();
if (file.status === Dropzone.UPLOADING) {
return Dropzone.confirm(_this.options.dictCancelUploadConfirmation, function () {
return _this.removeFile(file);
});
} else {
if (_this.options.dictRemoveFileConfirmation) {
return Dropzone.confirm(_this.options.dictRemoveFileConfirmation, function () {
return _this.removeFile(file);
});
} else {
return _this.removeFile(file);
}
}
});
file.previewElement.appendChild(file._removeLink);
}
attachmentsInputContainer.find('input').remove();
attachmentsInputContainer.append(Dropzone.instances[0].hiddenFileInput).find('input').attr('name', 'files');
return this._updateMaxFilesReachedClass();
},
This is default implementation of dropzone's addedfile option with 3 insertions.
Declared variable attachmentsInputContainer. This is invisible block. Something like
<div id="attachment_images" style="display:none;"></div>
Here I store future input with selected images
Then in the end of function remove previously added input(if any) from block and add new
attachmentsInputContainer.find('input').remove();
attachmentsInputContainer.append(Dropzone.instances[0].hiddenFileInput).find('input').attr('name', 'files');
And now, when you send form via simple submit button, input[name="files"] with values will be send.
I've made this hack because I append files to post that maybe not created yet
This is what I used for my past projects,
function makeDroppable(element, callback) {
var input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('multiple', true);
input.style.display = 'none';
input.addEventListener('change', triggerCallback);
element.appendChild(input);
element.addEventListener('dragover', function(e) {
e.preventDefault();
e.stopPropagation();
element.classList.add('dragover');
});
element.addEventListener('dragleave', function(e) {
e.preventDefault();
e.stopPropagation();
element.classList.remove('dragover');
});
element.addEventListener('drop', function(e) {
e.preventDefault();
e.stopPropagation();
element.classList.remove('dragover');
triggerCallback(e);
});
element.addEventListener('click', function() {
input.value = null;
input.click();
});
function triggerCallback(e) {
var files;
if(e.dataTransfer) {
files = e.dataTransfer.files;
} else if(e.target) {
files = e.target.files;
}
callback.call(null, files);
}
}
I am using the Latest version of Plupload (2.1) - UI widget
when i click upload files, it uploads all files correctly.
but when i try to upload files with my Form submit button. it doesn't work. it just showing a little bit uploading of files and then eventually submit the forms without completing the file upload.
here is my code :
jj = jQuery.noConflict();
jj(function() {
jj("#flash_uploader_other").plupload({
runtimes: "html5,flash,silverlight,html4",
url: "/external/uploader-new/upload.php",
max_file_size: "10mb",
chunk_size: "1mb",
unique_names: true,
filters: [
{
title: "jpg,xls,csv,doc,pdf,docx,xlsx",
extensions: "jpg,xls,csv,doc,pdf,docx,xlsx"
}
],
flash_swf_url: "/external/uploader-new/js/Moxie.swf",
silverlight_xap_url: "/external/uploader-new/js/Moxie.xap"
});
// Handle the case when form was submitted before uploading has finished
jj("form").submit(function(e) {
// Files in queue upload them first
if (jj("#flash_uploader_other").plupload("getFiles").length > 0) {
// When all files are uploaded submit form
jj("#flash_uploader_other").on("complete", function() {
jj("form").submit();
});
jj("#flash_uploader_other").plupload("start");
}
});
});
Please help!!
Thanks
its working now
I replaced the following code
jj("form").submit(function(e) {
// Files in queue upload them first
if (jj("#flash_uploader_other").plupload("getFiles").length > 0) {
// When all files are uploaded submit form
jj("#flash_uploader_other").on("complete", function() {
jj("form").submit();
});
jj("#flash_uploader_other").plupload("start");
}
});
With this code :
jj("form").submit(function(e) {
var uploader = jj("#flash_uploader_other").plupload("getUploader");
// Validate number of uploaded files
if (uploader.total.uploaded == 0) {
// Files in queue upload them first
if (uploader.files.length > 0) {
// When all files are uploaded submit form
uploader.bind("UploadProgress", function() {
jj("#flash_uploader_other").on("complete", function() {
jj("form").submit();
});
});
jj("#flash_uploader_other").plupload("start");
} else {
jj("form").submit();
}
//alert('You must at least upload one file.');
e.preventDefault();
}
});
Problem
I am currently using ( https://github.com/blueimp/jQuery-File-Upload/wiki ) this jQuery HTML5 Uploader.
The basic version, no ui.
The big problem is, that I looked everywhere (Mozilla Developer Network, SO, Google, etc.) and found no solution for removing a files already added via dragNdrop or manually via the file input dialogue.
Why do I want to achieve removing a file?
Because it seems that HTML5 has a kind of "bug".
If you drop / select a file (file input has set multiple) upload it, and then drop / select another file you magically have now the new file twice and it gets uploaded twice.
To prevent this magic file caching the use would have to refresh the page, which is not what someone wants to have for his modern AJAX web app.
What I have tried so far:
.reset()
.remove()
Reset Button
Setting .val() to ''
This seems to be a general HTML5 JS problem not jQuery specific.
Theory
Might it be, that $j('#post').click (I bind / re-bind a lot of times different callbacks), stacks the callbacks methods so that each time the updateFileupload function is called an additional callback is set.
The actual problem would now not rely anymore on the HTML5 upload, it would now rely on my could, miss-binding the .click action on my submit button (id=#post).
If we now call .unbind before each .click there shouldn't be any duplicated callback binding.
Code
Function containing the upload code:
function updateFileupload (type) {
var destination = "";
switch(type)
{
case upload_type.file:
destination = '/wall/uploadfile/id/<?=$this->id?>';
break;
case upload_type.image:
destination = '/wall/upload/id/<?=$this->id?>';
break;
}
$j('#fileupload').fileupload({
dataType: 'json',
url: destination,
singleFileUploads: false,
autoUpload: false,
dropZone: $k(".dropZone"),
done: function (e, data) {
console.log("--:--");
console.log(data.result);
upload_result = data.result;
console.log(upload_result);
console.log("--:--");
console.log(type);
if(type == upload_type.image)
{
var imageName = upload_result.real;
console.log(imageName);
$k.get('/wall/addpicture/id/<?=$this->id ?>/name'+imageName, function(data){
if(data > 0){
console.log("I made it through!");
if(!data.id)
{
$k('#imgUpload').html('');
//$k('#imgPreview').fadeOut();
$k('#newPost').val('');
$k.get("/wall/entry/id/"+data, function(html){
$k('#postList').prepend(html);
});
}
}
});
}
},
send: function(e, data){
var files = data.files;
var duplicates = Array(); // Iterate over all entries and check whether any entry matches the current and add it to duplicates for deletion
for(var i=0; i<data.files.length;i++)
{
for(var j=0;j<data.files.length-1;j++)
{
if(files[i].name == files[j].name && i != j)
{
duplicates.push(j);
}
}
}
if(duplicates.length > 0)
{
for(var i=0;i<duplicates.length;i++)
files.splice(i, 1);
}
console.log("Duplicates");
console.log(duplicates);
},
drop: function(e, data){
console.log("outside");
// $k.each(data.files, function(index, file){
// $k('#imageListDummy').after('<li class="file-small-info-box">'+file.name+'</li>');
// console.log(file);
//
// });
},
add: function(e, data){
upload_data = data;
console.log(data);
$k.each(data.files, function(index, file){
$k('#imageListDummy').after('<li class="file-small-info-box">'+file.name+'</li>');
console.log(file);
});
$j('#post').click(function(event){
upload_data.submit();
if(type == upload_type.image)
{
var file = upload_data.files[0];
console.log("I am here");
console.log(file);
var img = document.createElement("img");
img.src = window.URL.createObjectURL(file);
img.height = 64;
img.width = 64;
img.onload = function(e) {
window.URL.revokeObjectURL(this.src);
}
document.getElementById('imgPreview').appendChild(img);
$k('#imgPreview').show();
}
clickPostCallback(event);
});
$j('#showSubmit').show();
}
});
}
It could be more a browser security issue.
Current file uploads specs don't allow javascript (or anything as far as I know) to tamper with the value of the file field even if to remove it.
So I would imagine any good file uploader would create multiple file upload fields so you can remove the entire field rather than play with the value?
This is speculation though.
Updated answer to Updated Question:
Shouldn't click() only be bound once? you shouldn't need to rebind a click event to a single element '#post' (unless this element changes, in which case it should really be a class). You can place the click() event binding outside of the options for file upload, as long as it's contained in a $(function(){} so it's when the DOM's ready.
Aside from that I'm trying to read the code without any HTML and no experience in multiple file uploading. The best thing to do is try and re-create it on jsfiddle.net, that way others can go in and play around with the code without affecting you and your likely to find the problem while putting the code in there anyway :)