Is there any possibility that the multiple files will be uploaded one by one using dropzone.js. The following is a custom dropzone config script.
Dropzone.options.myDropzone = {
autoProcessQueue: false,
parallelUploads: 10,
addRemoveLinks:true,
init: function () {
var submitButton = document.querySelector("#submit-all");
myDropzone = this; // closure
submitButton.addEventListener("click", function () {
if(myDropzone.getQueuedFiles().length === 0)
{
alert("Please drop or select file to upload !!!");
}
else{
myDropzone.processQueue(); // Tell Dropzone to process all queued files.
}
});
},
url: "upload.php"
};
Right now, it uploads all files at a time which are all in the process queue. Since, the upload file size will be bigger, all files have to upload one by one. please help to short out the same.
I used this for uploading files one by one.
Hope this helps.
If you want the complete code according to your functions let me know.
startUpload() is called when customer confirms upload of files.
Dropzone.autoDiscover = false;
var myDropzone = new Dropzone("#uploadModal", {
url: "upload.php",
paramName: "file_upload",
maxFilesize: 1024,
maxFiles: 200,
autoProcessQueue: false
});
function startUpload(){
for (var i = 0; i < myDropzone.getAcceptedFiles().length; i++) {
myDropzone.processFile(myDropzone.getAcceptedFiles()[i]);
}
}
myDropzone.on('success', function(file, result) {
try {
result = JSON.parse(result)
if (!result.error) {
if(myDropzone.getQueuedFiles().length === 0 && myDropzone.getUploadingFiles().length === 0){
$("#uploadModal"). modal('hide');
myDropzone.removeAllFiles(true) ;
}
}
//TODO -
} catch (e) {
//TODO -
}
});
You need to set autoProcessQueue to true and parallelUploads to 1.
Setting autoProcessQueue to true tells dropzone to automatically process the queue. Setting parallelUploads to 1 tells dropzone to only upload one file at a time from the queue.
Dropzone.options.myDropzone = {
autoProcessQueue: true,
parallelUploads: 1,
addRemoveLinks:true,
init: function () {
var submitButton = document.querySelector("#submit-all");
myDropzone = this; // closure
submitButton.addEventListener("click", function () {
if(myDropzone.getQueuedFiles().length === 0)
{
alert("Please drop or select file to upload !!!");
}
else{
myDropzone.processQueue(); // Tell Dropzone to process all queued files.
}
});
},
url: "upload.php"
};
Related
I have an action class that runs across the entire app which handles file (images) uploads:
class UploadImageAction implements UploadImageContract
{
public function handle(Request $request, $imageProperty, $image, $imageDir)
{
if ($request->hasFile($imageProperty)) {
// Handle uploading lf_image
if (!is_null($image) && Storage::exists($image)) {
// Throw exceptions here
Storage::delete($image);
}
// Throw exceptions here
return $request->file($imageProperty)->store($imageDir);
}
}
}
And I resolve() this class withing the Service class:
public function handleAttachments($request, $report)
{
// Handle Attachments
$uploadImageAction = resolve(UploadImageAction::class);
// Handle attachment action
if($request->hasFile('attachment')) {
$report->attachment = $uploadImageAction->handle($request, 'attachment', $report->attachment, 'reports');
}
return $report;
}
Then passing it to the controller like so:
public function store(ReportsRequest $request, ReportService $reportService)
{
try
{
$reportService->storeReport($request);
return redirect('data-entry/reports')->with('success', 'Report Added Successfully');
}
catch (ImageUploadException $exception)
{
}
Reason for not calling handleAttachment() in the store() is because it's already passed with the validation within storeReport() method in Service class:
$report->fill($request->validated());
$report = $this->handleAttachments($request, $report);
$report->save();
This functionality works, but sinsce I tried adding Dropzone, that's where the issue happened.
the url of the dropzone is set like so: url: "{{ route('data-entry.reports.create') }}",. Also tried reports.store instead of .create
This is what I get in laravel debugbar:
and in the dev tools:
JS code:
// set the dropzone container id
const id = "#kt_dropzonejs_example_2";
const dropzone = document.querySelector(id);
// set the preview element template
var previewNode = dropzone.querySelector(".dropzone-item");
previewNode.id = "";
var previewTemplate = previewNode.parentNode.innerHTML;
previewNode.parentNode.removeChild(previewNode);
var myDropzone = new Dropzone(id, { // Make the whole body a dropzone
url: "{{ route('data-entry.reports.create') }}", // Set the url for your upload script location
parallelUploads: 20,
previewTemplate: previewTemplate,
maxFilesize: 1, // Max filesize in MB
autoQueue: false, // Make sure the files aren't queued until manually added
previewsContainer: id + " .dropzone-items", // Define the container to display the previews
clickable: id + " .dropzone-select" // Define the element that should be used as click trigger to select files.
});
myDropzone.on("addedfile", function (file) {
// Hookup the start button
file.previewElement.querySelector(id + " .dropzone-start").onclick = function () { myDropzone.enqueueFile(file); };
const dropzoneItems = dropzone.querySelectorAll('.dropzone-item');
dropzoneItems.forEach(dropzoneItem => {
dropzoneItem.style.display = '';
});
dropzone.querySelector('.dropzone-upload').style.display = "inline-block";
dropzone.querySelector('.dropzone-remove-all').style.display = "inline-block";
});
// Update the total progress bar
myDropzone.on("totaluploadprogress", function (progress) {
const progressBars = dropzone.querySelectorAll('.progress-bar');
progressBars.forEach(progressBar => {
progressBar.style.width = progress + "%";
});
});
myDropzone.on("sending", function (file) {
// Show the total progress bar when upload starts
const progressBars = dropzone.querySelectorAll('.progress-bar');
progressBars.forEach(progressBar => {
progressBar.style.opacity = "1";
});
// And disable the start button
file.previewElement.querySelector(id + " .dropzone-start").setAttribute("disabled", "disabled");
});
// Hide the total progress bar when nothing's uploading anymore
myDropzone.on("complete", function (progress) {
const progressBars = dropzone.querySelectorAll('.dz-complete');
setTimeout(function () {
progressBars.forEach(progressBar => {
progressBar.querySelector('.progress-bar').style.opacity = "0";
progressBar.querySelector('.progress').style.opacity = "0";
progressBar.querySelector('.dropzone-start').style.opacity = "0";
});
}, 300);
});
// Setup the buttons for all transfers
dropzone.querySelector(".dropzone-upload").addEventListener('click', function () {
myDropzone.enqueueFiles(myDropzone.getFilesWithStatus(Dropzone.ADDED));
});
// Setup the button for remove all files
dropzone.querySelector(".dropzone-remove-all").addEventListener('click', function () {
dropzone.querySelector('.dropzone-upload').style.display = "none";
dropzone.querySelector('.dropzone-remove-all').style.display = "none";
myDropzone.removeAllFiles(true);
});
// On all files completed upload
myDropzone.on("queuecomplete", function (progress) {
const uploadIcons = dropzone.querySelectorAll('.dropzone-upload');
uploadIcons.forEach(uploadIcon => {
uploadIcon.style.display = "none";
});
});
// On all files removed
myDropzone.on("removedfile", function (file) {
if (myDropzone.files.length < 1) {
dropzone.querySelector('.dropzone-upload').style.display = "none";
dropzone.querySelector('.dropzone-remove-all').style.display = "none";
}
});
As set in your route file (in the comment) the post route is named 'data-entry.reports.store'
So change the route:
var myDropzone = new Dropzone(id, { // Make the whole body a dropzone
url: "{{ route('data-entry.reports.store') }}", // Set the url for your upload script location
parallelUploads: 20,
previewTemplate: previewTemplate,
maxFilesize: 1, // Max filesize in MB
autoQueue: false, // Make sure the files aren't queued until manually added
previewsContainer: id + " .dropzone-items", // Define the container to display the previews
clickable: id + " .dropzone-select" // Define the element that should be used as click trigger to select files.
});
make sure to clear your route cache using php artisan route:clear
I figured out the issue
Since the image upload field is required, as well as the rest of the form, Dropzone doesn't read the uploaded file since there are some required fields haven't been filled!
Dropzone actually have a documentation about this:
https://docs.dropzone.dev/configuration/tutorials/combine-form-data-with-files
Since the files are combined with data in the Service class, I need to set autoProcessQueue to false and trigger it with the submit button like myDropzone.processQueue(); once all of the fields are filled to send everything to DB at once.
Using dropzone js when I try to sort server loaded files I receive the following error
Uncaught TypeError: Failed to execute 'readAsDataURL' on 'FileReader':
parameter 1 is not of type 'Blob'. at Dropzone.createThumbnail ... at Dropzone._processThumbnailQueue
I believe the error is something to do with pushing the incorrect mockFile variable into the dropzone files? Being an object rather than a File object which mockflow requires?
Below is the code I am currently using
function getFiles() {
$.getJSON('/listing/attachments/'+$('input[name="listing"]').val(),
function(data) {
if ( data ) {
$.each(data, function(i, item) {
var mockFile = {
name: item.name,
size: 23233,
status: 'success',
type: 'image/jpeg'
};
dropzone.emit("addedfile", mockFile);
dropzone.emit("thumbnail", mockFile, item.file);
dropzone.emit("complete", mockFile);
dropzone.files.push(mockFile);
});
}
});
}
var dropzone = new Dropzone(".dropzone", {
uploadMultiple: false,
parallelUploads: 100,
maxFilesize: 8,
maxFiles: 20,
addRemoveLinks: true,
acceptedFiles: ".png,.jpg,.gif,.bmp,.jpeg",
init:function() {
var self = this;
this.on("removedfile", function(file) {
$.ajax({
type: 'POST',
url: '/upload/delete',
data: {id: file.name, listing: $('input[name="listing"]').val(), _token: $('input[name="_token"]').val()},
dataType: 'html',
success: function(data){
var rep = JSON.parse(data);
if(rep.code == 200) {
}
}
});
} );
if ( $('input[name="listing"]').val() ) {
getFiles();
}
},
});
$(function(){
$(".dropzone").sortable({
items:'.dz-preview',
cursor: 'move',
opacity: 0.5,
containment: '.dropzone',
distance: 20,
tolerance: 'pointer',
update: function(e, ui){
var files = dropzone.files;
files.sort(function(a, b){
return ($(a.previewElement).index() > $(b.previewElement).index()) ? 1 : -1;
})
dropzone.removeAllFiles();
dropzone.handleFiles(files);
dropzone.processQueue();
}
});
});
Many thanks in advance :)
After having put a lot of hours into this I finally have a solution to make jquery sortable work with dropzone.js. I'll put the script of interest first and the full dropzone js script second. The commentary should explain what is happening.
init: function() {
// very important to make the sortable work
var myDropzone = this;
// In your drop zone you have your click handler event
document.getElementById("submit").addEventListener("click", function(e) {
// Make sure that the form isn't actually being sent.
e.preventDefault();
// the new array where we will put in the new files
var current_queue = [];
// the array we want to upgrade
var oldArray = myDropzone.files;
// on the webpage search for all the images that have been uploaded
var imageTags = $('#myDropzone').find('div.dz-image img');
// iterate through all the images that have been uploaded by the user
imageTags.each(function( index, imageTag ) {
// get the image name from the images
imageName = imageTag.alt;
// now we will iterate through the old array
var i;
for (i = 0; i < oldArray.length; i++) {
/** if the name of the image on the website is the same as the image from the old array
* we will add it to the new array. You can see this as sorting the array.
*/
if(imageName === oldArray[i].name){
current_queue.push(oldArray[i]);
}
}
});
/** after everything is done we will update the old array with the
* new array so it knows that the files have been sorted.
*/
myDropzone.files = current_queue;
// dropzone will now submit the request
e.stopPropagation();
myDropzone.processQueue();
});
if you are interested in the full dropzone js script:
$("#myDropzone").sortable({
opacity: 0.7,
});
Dropzone.options.myDropzone = {
// Configuration
url: '../somewhere',
method: 'post',
autoProcessQueue: false,
uploadMultiple: true,
parallelUploads: 100,
maxFiles: 100,
addRemoveLinks: true,
// The setting up of the dropzone
init: function() {
// very important to make the sortable work
var myDropzone = this;
// In your drop zone you have your click handler event
document.getElementById("submit").addEventListener("click", function(e) {
// Make sure that the form isn't actually being sent.
e.preventDefault();
// the new array where we will put in the new files
var current_queue = [];
// the array we want to upgrade
var oldArray = myDropzone.files;
// on the webpage search for all the images that have been uploaded
var imageTags = $('#myDropzone').find('div.dz-image img');
// iterate through all the images that have been uploaded by the user
imageTags.each(function( index, imageTag ) {
// get the image name from the images
imageName = imageTag.alt;
// now we will iterate through the old array
var i;
for (i = 0; i < oldArray.length; i++) {
/** if the name of the image on the website is the same as the image from the old array
* we will add it to the new array. You can see this as sorting the array.
*/
if(imageName === oldArray[i].name){
current_queue.push(oldArray[i]);
}
}
});
/** after everything is done we will update the old array with the
* new array so it knows that the files have been sorted.
*/
myDropzone.files = current_queue;
// dropzone will now submit the request
e.stopPropagation();
myDropzone.processQueue();
});
this.on('completemultiple', function(file, json) {
});
// sendingmultiple event
// of the sending event because uploadMultiple is set to true.
this.on("sendingmultiple", function(data, xhr, formData) {
formData.append("name", jQuery("#name").val());
formData.append("sample1", jQuery("#sample1").val());
});
this.on("successmultiple", function(files, response) {
// redirecting user on success. No message atm.
var url = document.location.origin + "/somewhere_to_redirect";
window.location.replace(url);
});
this.on("errormultiple", function(files, response) {
// Gets triggered when there was an error sending the files.
// Maybe show form again, and notify user of error
});
}
}
When I start uploading multiple files using the following code, addRemoveLinks: true gives the option to Cancel Upload. When I cancel any one of the files getting uploaded, it stops uploading all the files. It also shows total progress percentage as 100% after that.
Can anyone please help me understand what am I doing wrong? I want that on cancelling the upload for a single file should not affect the upload of other files.
How can I do this?
Dropzone.options.myAwesomeDropzone = {
autoProcessQueue: false,
uploadMultiple: true,
parallelUploads: 100,
maxFiles: 100,
maxFilesize: 1000,
addRemoveLinks: true,
// The setting up of the dropzone
init: function () {
var myDropzone = this;
this.element.querySelector("button[type=submit]").addEventListener("click", function (e) {
// Make sure that the form isn't actually being sent.
e.preventDefault();
e.stopPropagation();
var atLeastOneIsChecked = $('input:checkbox').is(':checked');
if(atLeastOneIsChecked)
myDropzone.processQueue();
else
alert("Please select a company!");
});
myDropzone.on("totaluploadprogress", function (progress) {
// Update progress bar with the value in the variable "progress", which
// is the % total upload progress from 0 to 100
$("#prog").html(progress);
});
myDropzone.on("drop", function (event) {
// Update progress bar with the value in the variable "progress", which
// is the % total upload progress from 0 to 100
$("#prog").html("0");
});
}
}
I have solved this problem by modifing the source code.
Dropzone.prototype.cancelUpload = function (file) {
var groupedFile, groupedFiles, _i, _j, _len, _len1, _ref;
if (file.status === Dropzone.UPLOADING) {
this.emit("canceled", file);
} else if ((_ref = file.status) === Dropzone.ADDED || _ref === Dropzone.QUEUED) {
file.status = Dropzone.CANCELED;
this.emit("canceled", file);
if (this.options.uploadMultiple) {
this.emit("canceledmultiple", [file]);
}
}
if (this.options.autoProcessQueue) {
return this.processQueue();
}
};
I have a dropzone script which uploads file to a certain directory. I did read a lot of the documentation already but still I'm stuck. I have this JS:
$(document).ready(function () {
Dropzone.autoDiscover = false;
var selectorElement = '.fileUploader';
var selector = $(selectorElement);
selector.addClass('dropzone');
selector.dropzone({
dictInvalidFileType: 'Kan bestand niet uploaden: ongeldige extensie.',
dictFileTooBig: 'Kan bestand niet uploaden: bestand te groot.',
dictResponseError: 'SERVER ERROR',
paramName: 'file[]', // The name that will be used to transfer the file
maxFilesize: 1000, // MB
url: 'ajax/uploadFile.php',
addRemoveLinks: true,
enqueueForUpload: true,
uploadMultiple: true,
sending: function (file, xhr, formdata) {
formdata.append('uploadPath', $(this)[0].element.dataset.uploadpath);
$(this).css({width: $(this)[0].element.getAttribute('width'), height: $(this)[0].element.getAttribute('height')});
},
init: function ()
{
var myDropzone = this;
this.on('maxfilesexceeded', function (file) {
this.removeFile(file);
});
this.on('removedfile', function (file) {
if (file.previewTemplate.children[6])
{
var filePath = this.element.dataset.uploadpath + file.previewTemplate.children[6].value;
$.post('ajax/deleteFile.php', {file: filePath}, function (response) {
CMSnotification(response.message, response.type);
}, 'JSON');
}
});
this.on('successmultiple', function (files, response) {
var responseJSON = JSON.parse(response);
CMSnotification(responseJSON.melding, responseJSON.type);
if (responseJSON.type === 'foutmelding')
{
for (var i = 0; i < files.length; i++)
{
var previewTemplate = $(files[i].previewTemplate);
previewTemplate.children('.dz-success-mark').css('display', 'none');
previewTemplate.children('.dz-error-mark').css('display', 'block');
previewTemplate.removeClass('dz-success').addClass('dz-error');
}
}
for (var i = 0; i < files.length; i++)
{
var previewTemplate = $(files[i].previewTemplate);
if (!responseJSON.files[i])
{
previewTemplate.children('.dz-success-mark').css('display', 'none');
previewTemplate.children('.dz-error-mark').css('display', 'block');
previewTemplate.removeClass('dz-success').addClass('dz-error');
}
else
{
previewTemplate.append('<input type="hidden" name="fileNames[]" value="' + responseJSON.fileNames[i] + '">');
previewTemplate.append('<input type="hidden" name="extensions[]" value="' + responseJSON.extensions[i] + '">');
}
}
});
},
accept: function (file, done) {
var extension = file.name.split('.').pop();
extension = extension.toLowerCase();
if ($.inArray(extension, window.allowedFileDropzoneExtensions) > -1) {
done();
}
else {
done('Bestands extensie niet toegestaan.');
}
}
});
});
The first problem is, I include this file once and still it gives the error dropzone already attached. But the biggest problem is:
The dropzone is pretty inconsistent. For 3 files, it calls 3 request files. For 8 files, it can call 4 request files. But it should just call 1. The problem is, if I allow the user to give a callback as parameter in the function and call it in the success event of the upload, it will call that callback multiple times (but it should just call it once).
The first problem is because you have the statement Dropzone.autoDiscover = false inside the .ready() function, and this causes to trigger it to late. Move this statement outside the .ready() function.
Dropzone.autoDiscover = false;
$(document).ready(function () {
.........
})
The second is due to the way dropzone works, by default the option autoProcessQueue is set to true, and dropzone uploads the files immediately after they are added. I think there is no guarantee of how many files will upload at once, but I think that by default will never upload more than two.
A solution is to manually trigger the upload of the files, to do this you need to set autoProcessQueue to false, just to mention an example you can use a button that triggers the upload, inside the init option add an event listener for the button that triggers the processQueue() method. Here an example of this solution:
html:
<button type="button" id="submit-all">Submit All</button>
js:
selector.dropzone({
autoProcessQueue: false,
uploadMultiple: true,
init: function () {
var submitButton = document.querySelector("#submit-all");
myDropzone = this;
submitButton.addEventListener("click", function () {
myDropzone.processQueue();
});
}
})
I just included the relevant part for this to work, you can add this to your existing configuration. This will also solve your problem for the successmultiple event.
I'm struggling with the plugin JQuery-File-upload.
I would like to divide the upload process in two steps.
Step1 - when a file is selected in the input[type=file], I would
like to test its type and its size. The only accepted files should be
image files with a size < 4MB.
If the file does not match with these constraints a popup is displayed with an error message.
If the file is OK I display the name of the file in the input[type=text]
Step2 - when the user click on the button "OK", the upload of the
file start
I have the following code
$('#file-img').fileupload({
dataType: 'json',
autoUpload: false,
formData: {type: 'businessPicture'},
add: function (e, data) {
var uploadErrors = [];
var acceptFileTypes = /^image\/(gif|jpe?g|png)$/i;
if(data.originalFiles[0]['type'].length && !acceptFileTypes.test(data.originalFiles[0]['type'])) {
uploadErrors.push('Not an accepted file type');
}
if(data.originalFiles[0]['size'].length && data.originalFiles[0]['size'] > 4000000) {
uploadErrors.push('File size is too big');
}
if(uploadErrors.length > 0) {
alert(uploadErrors.join("\n"));
} else {
$.each(data.files, function (index, file) {
$("#txt-file-img").val(file.name);
});
//I COMMENT THIS LINE because if I do not, the upload start here.
//data.submit();
}
},
done: function (e, data) {
$("#output").html('<p class="valid">SUCCESS!</p>');
$.each(data.result.files, function (index, file) {
$("#preview-picture").css("max-width","160px");
$("#preview-picture").css("max-height","150px");
$("#preview-picture").attr("src",file.url+'#'+ new Date().getTime());
});
},
progressall: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
$('#upload-progress .bar').css(
'width',
progress + '%'
).text(
progress + '%'
);
},
fail: function (e, data) {
$("#output").html('<p class="error">FAIL!</p>');
}
});
});
I don't understand most of the example provided on the Plugin website.
With the code above, the control (size & type) are OK but I don't know how to start the upload only after clicking on the button.
According to you what is the best way to manage this behaviour?
Thanks a lot
OK it's pretty easy. I just have to bind the click event on the button with the action data.submit directly under the event add of the instance $('#file-img').fileupload().
$('#file-img').fileupload({
dataType: 'json',
autoUpload: false,
formData: {type: 'businessPicture'},
add: function (e, data) {
var uploadErrors = [];
var acceptFileTypes = /^image\/(gif|jpe?g|png)$/i;
if(data.originalFiles[0]['type'].length && !acceptFileTypes.test(data.originalFiles[0]['type'])) {
uploadErrors.push('Not an accepted file type');
}
if(data.originalFiles[0]['size'].length && data.originalFiles[0]['size'] > 4000000) {
uploadErrors.push('File size is too big');
}
if(uploadErrors.length > 0) {
alert(uploadErrors.join("\n"));
} else {
$.each(data.files, function (index, file) {
$("#txt-file-img").val(file.name);
});
$("#btn_add_valid").on('click',function () {
data.submit();
});
}
},
//The rest of the function....