0

I'm working on a project (in Django) where I have created a page to add data information about a file and then add the file itself.

enter image description here


When 'More datasets' button is clicked, it adds another field to upload another file.

enter image description here

This can be done to attach as many files as the end-user wants in one go.
What I need is to upload all the attached files once 'Upload data-sets' is clicked and individual progress bar should be displayed.

So far, I have run through multiple tutorials but came kinda close using Vitor Freitas's tutorial.
JS code :

$(function(){ /*$("#add_new_dataset").click(function(){ $(".file").click(); });*/ $(".file").fileupload({ dataType: 'json', sequentialUploads: true, /* Send the files one by one */ start: function(e){ /* When the upload process starts, show the modal */ $("#modal-progress").modal("show"); }, stop: function(e){ /* When the upload progress finalizes, hide the modal */ $("#modal-progress").modal("hide"); }, progressall: function(e, data){ /* Update the progress bar */ var progress = parseInt(data.loaded / data.total * 100, 10), strProgress = progress + "%"; $(".progress-bar").css({"width": strProgress}); $(".progress-bar").text(strProgress); }, done: function(e, data){ if(data.result.is_valid){ $("#gallery tbody").prepend( "<tr><td><a href='" + data.result.url + "'>" + data.result.name + "</a></td></tr>" ); } } }) }); 

Template code :

<form id="form" method="POST" enctype="multipart/form-data"> {% csrf_token %} <div class="container-fluid" id="datasets" style="margin: 0px; padding: 0px;"> <div class="row" onfocusin="populateFilename(this);"> <label class="col-md-2">User Name :</label> <input class="col-md-2" type="text" id="username" name="user_name" value="{{ user.username }}" readonly /> <label class="col-md-2">Data-set :</label> <input class="col-md-2" type="text" placeholder="Enter dataset" name="dataset" required /> <label class="col-md-2">Creation Date :</label> <input class="col-md-2" type="date" placeholder="YYYY-MM-DD" name="creation_date" required /> <label class="col-md-2">Beam Line:</label> <input class="col-md-2" type="text" placeholder="Enter beam line" name="beamline" required /> <label class="col-md-2">Data-set file:</label> <input class="col-md-2 file" type="file" name="file" data-url="{% url 'add_data_sets' %}" data-form-data='{"csrfmiddlewaretoken": "{{ csrf_token }}"}' required /> <label class="filename"></label> <div class="modal fade" id="modal-progress" data-backdrop="static" data-keyboard="false"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h4 class="modal-title">Uploading...</h4> </div> <div class="modal-body"> <div class="progress"> <div class="progress-bar" role="progressbar" style="width: 0%;">0%</div> </div> </div> </div> </div> </div> </div> </div> <div style="align: center; margin-top: 5px;"> <div class="container btn-group btn-group-justified btn-group-lg"> <a onmouseup="addRow();" class="btn btn-outline-secondary" style="width: 50%;">More Datasets</a> <button type="submit" class="btn btn-outline-primary" name="add_new_dataset" id="add_new_dataset">Submit Data-set</button> </div> </div> 


What should I do? I'm not very good at AJAX but I can't see any code that works to send the data to the server side. Is it so or am I missing something? Kindly ignore my ignorance on the topic and thanks to all in advance.

EDIT :
The JS code has been rewritten based on some of the answers.

document.getElementById('add_new_dataset').onclick = function() { $(this).preventDefault(); console.log('Files uploading begin'); form_data = new FormData(); const files = document.getElementsByName('file'); let count = 0; for(let i = 0; i < files.length; i++){ count++; form_data.append("file", files[i]); } $.ajax({ url: "/add_data_sets", dataType: 'json', contentType: false, processData: false, data: form_data, type: 'POST', success: function(files, response, xhr, pd){ $('.file').show(); if(files.status != false){ $('.progress-bar').val('/location/' + files.filename); var fileData = files.filename; console.log('Files uploading...'); } else{ alert(files.filename); } }, /*xhrFields: { onprogress: function(e) { if(e.lengthComputable) { let percentCompleted = e.loaded / evt.total; pBar.style.width = percentComplete; pBar.innerHTML = percentComplete + "%"; console.log('Percent complete : ' + percentComplete); } } }*/ xhr: function(){ let xhr = $.ajaxSettings.xhr(); xhr.upload.onprogress = function(e) { let percentCompleted = e.loaded / evt.total * 100; pBar.style.width = percentComplete; pBar.innerHTML = percentComplete + "%"; console.log('Percent complete : ' + percentComplete); }; return xhr; } }); //}); }; 

This is just the upload code block. The sending of the data from client-side to server-side works perfectly but it makes be suspicious as the 'console.log' calls aren't being triggered when the code runs through it. Is it so that the data is, somehow, being submitted normally and this code is doing nothing.

EDIT2 : A new JS function:

function upload() { console.log('Upload function begins'); let pBar = document.getElementsByClassName('progress-bar')[0], progressWindow = document.getElementById('modal-progress'), formData = new FormData(document.forms.form), xhr = new XMLHttpRequest(), percent = 0; console.log('Form Data created'); // Start upload xhr.upload.onloadstart = function() { //$('#modal-progress').hide().fadeIn(); //progressWindow }; // Track upload progress xhr.upload.onprogress = function(event) { percent = parseInt(event.loaded / event.total * 100); pBar.innerHTML = percent + "%"; pBar.style.width = percent + "%"; //console.log(percent + '% completed'); //console.log('Uploaded event.loaded of event.total'); }; // Report if ends with an error xhr.upload.onerror = function() { console.log('An error has occurred') }; // Track completion: Both successful or not xhr.upload.onloadend = function() { //$('#modal-progress').fadeOut().hide(); console.log('Upload complete with or without error ' + xhr.status); }; // Track progress: Triggered on successful completion xhr.upload.onload = function() { console.log('Uploading complete'); progressWindow.hidden = True; }; xhr.open("POST", "{% url 'add_data_sets' %}", true); // The 'setRequestHeader' function can only be called when xhr is opened. //xhr.setRequestHeader('csrfmiddlewaretoken', '{{ csrf_token }}'); //xhr.setRequestHeader('test-info', 'something'); xhr.setRequestHeader('Content-Type', 'application/gzip'); xhr.send(formData); } 


The function works fine now. It sends the data completely fine but on the development server console screen I get this error.

Forbidden (CSRF token missing or incorrect.): /accounts/add_data_set/ [22/Feb/2020 15:36:06] "POST /accounts/add_data_set/ HTTP/1.1" 403 2513 

I even checked logged the POST data being send to the server and it does contain the csrf token

<QueryDict: {'csrfmiddlewaretoken': ['WREoIV0aY4B2XyrU7d9Qw8kMwiokXqwWsmbc2QSHX5VQ0EaYjjeuv7PeysMJjecp'], 'user_name': ['rakesh'], 'dataset': ['r'], 'creation_date': ['2020-02-22'], 'beamline': ['r']}> 

I'm kind of confused. Is this an issue?

8
  • Have you added the scripts jquery.ui.widget.js and jquery.fileupload.js somewhere? And note that this is to upload a file immediately when you select a file using the "browse" button, this is not for submitting different files for different input fields when pressing your submit form button. Commented Feb 20, 2020 at 11:45
  • As shown in the answer below, I would just submit the entire form with its files when the 'upload data-sets' button is clicked. You don't need any of the scripts from the tutorial for that, only jquery. Commented Feb 20, 2020 at 11:47
  • @dirkgroten yes, I had added the jquery.ui.widget.js and jquery.fileupload.js script in the template. In your second comment, do you mean that for pushing data and the progress bar visualization I don't need those script? Will it all be handled by jQuery? Commented Feb 21, 2020 at 5:06
  • Yes, you don't need those scripts, everything is now handled in your jquery code. It sounds like indeed, your form is still submitted "normally" by the browser, rather than triggering your javascript. You can verify that by looking in your browser dev tools, network tab: If you see a "document" request for the POST request, it means your browser submitted the form. If $.ajax() submits the form, you'd see an XHR request. Either change the event handling to $("#form").on("submit", function() {...}) (instead of the click event on the button) or remove type="submit" on your button. Commented Feb 21, 2020 at 12:05
  • @dirkgroten I tried the technique of removing type="submit". It worked fine but the page stopped refreshing. I have used the technique I've included in my second edit and I'm facing an issue. Commented Feb 22, 2020 at 18:41

1 Answer 1

1

If you have any fileuploader plugin their documentations will have everything, or if you want normal file upload input you can post them by binding file input to form data and then post on action which will manipulate form data and save images, then you can return save image and display, this way you can achieve simple ajax upload.

var form_data = new FormData(); var totalFiles = document.getElementById('file').files.length; var count = 0; for (var i = 0; i < totalFiles; i++) { var file = document.getElementById('file').files[i]; count++; form_data.append("file", file); } $.ajax({ url: "/uploadingaction", dataType: 'json', contentType: false, processData: false, data: form_data, type: 'POST', success: function (files, response, xhr, pd) { $('yourloaderid').hide(); if (files.status != false) { $('#displayid').val('/location/' + files.filename); var filedata = files.filename; } else { alert(files.filename); } } }) 
Sign up to request clarification or add additional context in comments.

6 Comments

Note that this code should be wrapped in $("#add_new_dataset").on("click", function() {...}) and there should also be a $(this).preventDefault() to prevent the button from submitting the form.
Add the xhr option to add progress behavior, as shown here
@dirkgroten thanks for all of your help. I've made some edits to the JS function based on what I understood but it still isn't working.
@nikesh thank you for such a quick response. In command ` $('yourloaderid').hide(); ` should 'yourloaderid' represent the id of the filepicker? Why is it being hidden?
@MayankMamgaain actually this is normal loading screen or progress bar you can say which can hide after ajax successful loaded.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.