Multiple file upload - with 'remove file' link - javascript

I'm trying to create a form where I can have multiple file upload sections, where the user can upload multiple files.
That part, is reasonably straight forward. My problem comes from allowing the user to 'remove' a file from the upload list, before it's uploaded.
I've created a fiddle to illustrate
http://jsfiddle.net/alexjamesbrown/o62srbew/
I've got a simple row, that holds the <input type="file"
<div class="row files" id="files1">
<h2>Files 1</h2>
<span class="btn btn-default btn-file">
Browse <input type="file" name="files1" multiple />
</span>
<br />
<ul class="fileList"></ul>
</div>
then, so far, I've created a jquery plugin so it's re-usable:
$.fn.fileUploader = function (filesToUpload) {
this.closest(".files").change(function (evt) {
for (var i = 0; i < evt.target.files.length; i++) {
filesToUpload.push(evt.target.files[i]);
};
var output = [];
for (var i = 0, f; f = evt.target.files[i]; i++) {
var removeLink = "Remove";
output.push("<li><strong>", escape(f.name), "</strong> - ",
f.size, " bytes. ", removeLink, "</li> ");
}
$(this).children(".fileList")
.append(output.join(""));
});
};
I'm then initialising my very basic plugin like this:
var filesToUpload = [];
$("#files1").fileUploader(filesToUpload);
$("#files2").fileUploader(filesToUpload);
$("#uploadBtn").click(function (e) {
e.preventDefault();
});

As in this JSFiddle, I've added a class name .removeFile to the dynamically generated remove link; then use this class as a selector to pick up the one which is clicked and remove the parent li.
Updated:
JS:
// add .removeFile class to the li's element to pick them by this selector
var removeLink = "<a class=\"removeFile\" href=\"#\" data-fileid=\"" + i + "\">Remove</a>";
output.push("<li><strong>", escape(f.name), "</strong> - ",
f.size, " bytes. ", removeLink, "</li> ");
}
$(this).children(".fileList")
.append(output.join(""));
});
};
var filesToUpload = [];
$(document).on("click",".removeFile", function(e){
e.preventDefault();
var fileName = $(this).parent().children("strong").text();
// loop through the files array and check if the name of that file matches FileName
// and get the index of the match
for(i = 0; i < filesToUpload.length; ++ i){
if(filesToUpload[i].name == fileName){
//console.log("match at: " + i);
// remove the one element at the index where we get a match
filesToUpload.splice(i, 1);
}
}
//console.log(filesToUpload);
// remove the <li> element of the removed file from the page DOM
$(this).parent().remove();
});
You can un-comment the console.log() statements to see the result

Related

How can I do a multi file upload in Django, allowing user to remove file upload before form submission?

I am trying to do a fairly common user requirement, allowing the user to upload multiple files in Django, and giving them the option to remove files that they may have accidentally uploaded.
As far as I can tell, even if the user remove the uploaded files from the DOM prior to the user submitting the form via Javascript code as shown here...Multiple file upload - with 'remove file' link, the uploaded files remain in the request.FILES.getlist(''). I have spent most of today researching this. I have in fact determined that when the uploaded files are deleted via Javascript from the DOM they are in fact still present in the request.FILES.getlist('').
I verified this by printing out the contents of the getlist file in my CreateView as shown below...
list=[] #myfile is the key of a multi value dictionary, values are the uploaded files
for f in request.FILES.getlist('files1'): #myfile is the name of your html file button
filename = f.name
print(filename)
My question is how can I get the contents of the DOM and compare it to what's in request.FILES.getlist? I surmise that's how I'll be able to tell Django what's real and what's not. Thanks in advance for any thoughts.
Here's the Javascript code that I'm using...I documented it via a link above but here it is again for ease of reading. The Javascript seems to work just fine...it's just that the files are still in request.FILES.getlist. I suspect this is specific to Django/Python. Something additional needs to happen in order to reconcile the DOM with what actually remains in request.FILES.getlist.
$(document).ready(function (){
$.fn.fileUploader = function (filesToUpload) {
this.closest(".files").change(function (evt) {
for (var i = 0; i < evt.target.files.length; i++) {
filesToUpload.push(evt.target.files[i]);
};
var output = [];
for (var i = 0, f; f = evt.target.files[i]; i++) {
var removeLink = "<a class=\"removeFile\" href=\"#\" data-fileid=\"" + i + "\">Remove</a>";
output.push("<li><strong>", escape(f.name), "</strong> - ",
f.size, " bytes. ", removeLink, "</li> ");
}
$(this).children(".fileList")
.append(output.join(""));
});
};
var filesToUpload = [];
$(document).on("click",".removeFile", function(e){
e.preventDefault();
console.log("Htell");
var fileName = $(this).parent().children("strong").text();
for(i = 0; i < filesToUpload.length; ++ i){
if(filesToUpload[i].name == fileName){
filesToUpload.splice(i, 1);
}
}
$(this).parent().remove();
});
$("#files1").fileUploader(filesToUpload);
});
The HTML...
<div class="leftwidth22">
<div class="width52">
<h2 class="floatright23">Attachment(s) - </h2>
</div>
</div>
<div class="rightwidth60">
<h2 class="width70">
<div class="row files" id="files1">
<span class="">
<input type="file" name="files1" multiple />
</span>
<br />
<ul class="fileList"></ul>
</div>
</h2>
</div>
Here's my view as well...it's a CreateView...and most of the work is happening in POST...
def post(self, request, *args, **kwargs):
if "cancel" in request.POST:
return HttpResponseRedirect(reverse('Procedures:procedure_main_menu'))
else:
self.object = None
user = request.user
form_class = self.get_form_class()
form = self.get_form(form_class)
file_form = NewProcedureFilesForm(request.POST, request.FILES)
files = request.FILES.getlist('files1') #field name in model
if form.is_valid() and file_form.is_valid():
procedure_instance = form.save(commit=False)
procedure_instance.user = user
procedure_instance.save()
list=[] #myfile is the key of a multi value dictionary, values are the uploaded files
for f in request.FILES.getlist('files1'): #myfile is the name of your html file button
filename = f.name
print(filename)
for f in files:
procedure_file_instance = NewProcedureFiles(attachments=f, new_procedure=procedure_instance)
procedure_file_instance.save()
return self.form_valid(form)
else:
form_class = self.get_form_class()
form = self.get_form(form_class)
file_form = NewProcedureFilesForm()
return self.form_invalid(form)

How to upload multiple PDF files using jQuery?

I'm trying to upload multiple pdf files using php, but the following code is allowing to upload only img, png....I have added application/pdf but only one PDF file is being uploaded, not multiple. I want to upload multiple PDF files same as this code is uploading accepted files, selecting one by one and all at once.
Can you please help me to upload multiple pdf files and upload in sql?
Thank you.
<div>
<label style="font-size: 14px;">
<span style='color:navy;font-weight:bold'>Attachment Instructions :</span>
</label>
<ul>
<li>
Allowed only files with extension (jpg, png, gif)
</li>
<li>
Maximum number of allowed files 10 with 300 KB for each
</li>
<li>
you can select files from different folders
</li>
</ul>
<!--To give the control a modern look, I have applied a stylesheet in the parent span.-->
<span class="btn btn-success fileinput-button">
<span>Select Attachment</span>
<input type="file" name="files[]" id="files" multiple accept="image/jpeg, image/png, image/gif,"><br />
</span>
<output id="Filelist"></output>
</div>
document.addEventListener("DOMContentLoaded", init, false);
//To save an array of attachments
var AttachmentArray = [];
//counter for attachment array
var arrCounter = 0;
//to make sure the error message for number of files will be shown only one time.
var filesCounterAlertStatus = false;
//un ordered list to keep attachments thumbnails
var ul = document.createElement("ul");
ul.className = "thumb-Images";
ul.id = "imgList";
function init() {
//add javascript handlers for the file upload event
document
.querySelector("#files")
.addEventListener("change", handleFileSelect, false);
}
//the handler for file upload event
function handleFileSelect(e) {
//to make sure the user select file/files
if (!e.target.files) return;
//To obtaine a File reference
var files = e.target.files;
// Loop through the FileList and then to render image files as thumbnails.
for (var i = 0, f; (f = files[i]); i++) {
//instantiate a FileReader object to read its contents into memory
var fileReader = new FileReader();
// Closure to capture the file information and apply validation.
fileReader.onload = (function(readerEvt) {
return function(e) {
//Apply the validation rules for attachments upload
ApplyFileValidationRules(readerEvt);
//Render attachments thumbnails.
RenderThumbnail(e, readerEvt);
//Fill the array of attachment
FillAttachmentArray(e, readerEvt);
};
})(f);
// Read in the image file as a data URL.
// readAsDataURL: The result property will contain the file/blob's data encoded as a data URL.
// More info about Data URI scheme https://en.wikipedia.org/wiki/Data_URI_scheme
fileReader.readAsDataURL(f);
}
document
.getElementById("files")
.addEventListener("change", handleFileSelect, false);
}
//To remove attachment once user click on x button
jQuery(function($) {
$("div").on("click", ".img-wrap .close", function() {
var id = $(this)
.closest(".img-wrap")
.find("img")
.data("id");
//to remove the deleted item from array
var elementPos = AttachmentArray.map(function(x) {
return x.FileName;
}).indexOf(id);
if (elementPos !== -1) {
AttachmentArray.splice(elementPos, 1);
}
//to remove image tag
$(this)
.parent()
.find("img")
.not()
.remove();
//to remove div tag that contain the image
$(this)
.parent()
.find("div")
.not()
.remove();
//to remove div tag that contain caption name
$(this)
.parent()
.parent()
.find("div")
.not()
.remove();
//to remove li tag
var lis = document.querySelectorAll("#imgList li");
for (var i = 0; (li = lis[i]); i++) {
if (li.innerHTML == "") {
li.parentNode.removeChild(li);
}
}
});
});
//Apply the validation rules for attachments upload
function ApplyFileValidationRules(readerEvt) {
//To check file type according to upload conditions
if (CheckFileType(readerEvt.type) == false) {
alert(
"The file (" +
readerEvt.name +
") does not match the upload conditions, You can only upload jpg/png/gif files"
);
e.preventDefault();
return;
}
//To check file Size according to upload conditions
if (CheckFileSize(readerEvt.size) == false) {
alert(
"The file (" +
readerEvt.name +
") does not match the upload conditions, The maximum file size for uploads should not exceed 300 KB"
);
e.preventDefault();
return;
}
//To check files count according to upload conditions
if (CheckFilesCount(AttachmentArray) == false) {
if (!filesCounterAlertStatus) {
filesCounterAlertStatus = true;
alert(
"You have added more than 10 files. According to upload conditions you can upload 10 files maximum"
);
}
e.preventDefault();
return;
}
}
//To check file type according to upload conditions
function CheckFileType(fileType) {
if (fileType == "image/jpeg") {
return true;
} else if (fileType == "image/png") {
return true;
} else if (fileType == "image/gif") {
return true;
} else {
return false;
}
return true;
}
//To check file Size according to upload conditions
function CheckFileSize(fileSize) {
if (fileSize < 300000) {
return true;
} else {
return false;
}
return true;
}
//To check files count according to upload conditions
function CheckFilesCount(AttachmentArray) {
//Since AttachmentArray.length return the next available index in the array,
//I have used the loop to get the real length
var len = 0;
for (var i = 0; i < AttachmentArray.length; i++) {
if (AttachmentArray[i] !== undefined) {
len++;
}
}
//To check the length does not exceed 10 files maximum
if (len > 9) {
return false;
} else {
return true;
}
}
//Render attachments thumbnails.
function RenderThumbnail(e, readerEvt) {
var li = document.createElement("li");
ul.appendChild(li);
li.innerHTML = [
'<div class="img-wrap"> <span class="close">×</span>' +
'<img class="thumb" src="',
e.target.result,
'" title="',
escape(readerEvt.name),
'" data-id="',
readerEvt.name,
'"/>' + "</div>"
].join("");
var div = document.createElement("div");
div.className = "FileNameCaptionStyle";
li.appendChild(div);
div.innerHTML = [readerEvt.name].join("");
document.getElementById("Filelist").insertBefore(ul, null);
}
//Fill the array of attachment
function FillAttachmentArray(e, readerEvt) {
AttachmentArray[arrCounter] = {
AttachmentType: 1,
ObjectType: 1,
FileName: readerEvt.name,
FileDescription: "Attachment",
NoteText: "",
MimeType: readerEvt.type,
Content: e.target.result.split("base64,")[1],
FileSizeInBytes: readerEvt.size
};
arrCounter = arrCounter + 1;
}

how to retrieve data length in console log

how to get data length inside Files ? i need data length to make alert maximize file
I want to limit the maximum upload of image files by adding alerts to the following code
the alert is alert("You Have Reached The MAXIMUM Upload Limit"); so when I add one by one upload image to the specified limit, an alert will appear
i want to get data length after click upload one by one but the data length is always 0 why ?
html
<div class="files col-sm-4" id="files1">
<input type="file" name="files1" id="imageten" multiple />
<br />Selected files:
<ol class="fileList"></ol>
</div>
script
$.fn.fileUploader = function (filesToUpload) {
this.closest(".files").change(function (evt) {
for (var i = 0; i < evt.target.files.length; i++) {
filesToUpload.push(evt.target.files[i]);
};
var output = [];
for (var i = 0, f; f = evt.target.files[i]; i++) {
var removeLink = "<i class=\"fa fa-times fa-close removeFile\" href=\"#\" data-fileid=\"" + i + "\"></i>";
output.push("<li><strong>", escape(f.name), "</strong> ", removeLink, " </li> ");
}
$(this).children(".fileList").append(output.join(""));
console.log(evt.target.files);
});
};
var filesToUpload = [];
$(document).on("click",".removeFile", function(e){
e.preventDefault();
var fileName = $(this).parent().children("strong").text();
for(i = 0; i < filesToUpload.length; ++ i){
if(filesToUpload[i].name == fileName){
filesToUpload.splice(i, 1);
console.log(filesToUpload);
}
}
$(this).parent().remove();
});
There is no variable named data in your code. I did not understood where you are returning 0. But, if my guess is correct, add this line inside $(document).on("click",".removeFile", function(e){
$(this).children(".fileList").append(output.join(""));
console.log(evt.target.files);
alert(evt.target.files.length); // add this line
$("#fileinput")[0].files.length will give the number of files.
if($("#fileinput")[0].files.length > 2) {
alert("You can select only 2 files");
}
Let me know if this is helpful.
How to limit the maximum files chosen when using multiple file input - There are already questions like this on stackoverflow.

How to fire cancel on open dialog file input on jquery

Hi all i have code for multiple upload image, but i want only one image per upload. so I create the input file every time I clicked the upload button with the dynamic id. however I have problems checking whether the user chooses the file to upload or press the cancel button. because if the user pressed the cancel button I want to delete the input file I have created. for full sourcenya as below:
$(document).ready(function () {
$("#btnimg").click(function () {
//check input file id number
var counter = $("input[id^='upload']").length;
//add input file every btnimg clicked
var html = "<input type='file' id='upload_" + counter + "' style='display:none;'/>";
$("#result").append(html);
//trigger to dialog open file
var upload = $('#upload_' + counter);
upload.trigger('click');
upload.on('change', function () {
console.log('change fire...');
var inputFiles = this.files;
var inputFile = inputFiles[0];
var reader = new FileReader();
reader.onload = function (evt) {
var imghtml = "<img id='img_upload_" + counter + "' src='" + evt.target.result + "' width='50px;' height='50px;'/>";
$('#previewimage').append(imghtml);
};
reader.onerror = function (event) {
alert("something: " + event.target.error.code);
};
reader.readAsDataURL(inputFile);
});
//if file not selected or user press button cancel on open dialog
//upload.remove();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div id="result"></div>
<button id="btnimg">upload image</button>
<div id="previewimage">
</div>
</body>
thank you in advance,
You can check the .length of <input type="file"> element .files property to determine if a file is selected by user
That all sounds like an xy-problem to me.
I have not (yet) got a response from you about the why you want to do it, so I will base my answer on two probable situations:
If you want to keep track of the selected Files, in order to be able to do anything with them later (e.g send them through AJAX), then use a single <input>.
At every change event, you will store the new File in an Array, from where you will also be able to do something with later on:
(function() {
// this Array will hold our files, should be accessible to the final function 'doSomething'
var savedFiles = [];
var counter = 0;
var upload = $('#upload');
upload.on('change', onuploadchange);
$("#btnimg").click(function routeClick() {
upload.trigger('click');
});
$('#endbtn').click(function doSomething() {
console.log(savedFiles);
});
function onuploadchange() {
var inputFiles = this.files;
var inputFile = inputFiles[0];
if (!inputFile) { return; } // no File ? return
savedFiles.push(inputFile); // save this File
// don't use a FileReader here, useless and counter-productive
var url = URL.createObjectURL(inputFile);
var imghtml = "<img id='img_upload_" + counter + "' src='" + url + "' width='50px;' height='50px;'/>";
$('#previewimage').append(imghtml);
$('#endbtn').removeAttr('disabled');
}
})();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="result">
<!-- A single input to save them all-->
<input type='file' id='upload' style='display:none;' />
</div>
<button id="btnimg">upload image</button>
<div id="previewimage">
</div>
<button id="endbtn" disabled>do something with saved files</button>
If, for an obscure reason, you absolutely need to keep all the filled <input> elements in your document, then create a new one only if the last one is itself filled.
$(document).ready(function() {
$("#btnimg").click(function() {
// grab previous ones
var inputs = $("input[id^='upload']");
// get the last one we created
var last = inputs.last();
var counter = inputs.length;
console.log(counter);
var upload;
// if there is no input at all, or if the last one is already filled with a File
if (!last.length || last[0].files.length) {
console.log('create new input');
upload = makeNewInput();
} else {
// use the last one
upload = last;
}
//trigger to dialog open file
upload.trigger('click');
function makeNewInput(counter)  {
var html = "<input type='file' id='upload_" + counter + "' style='display:none;'/>";
var el = $(html);
el.on('change', onuploadchange);
$('#result').append(el);
return el;
}
function onuploadchange() {
var inputFiles = this.files;
var inputFile = inputFiles[0];
var url = URL.createObjectURL(inputFile);
var imghtml = "<img id='img_upload_" + counter + "' src='" + url + "' width='50px;' height='50px;'/>";
$('#previewimage').append(imghtml);
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="result"></div>
<button id="btnimg">upload image</button>
<div id="previewimage">
</div>

multi upload images in same sequence as user input

I have a multi-upload for image files, and the problem that I used to face was that the images are appearing in a different sequence as the user's input. For example, user selects Img1, Img2, Img3, Img4. The sequence that it appears might be Img2, Img4, Img3, Img1.
This causes a problem as I have to link them to a text field (image description), and each text field has to match the right image. I did some digging and found out that i can use this code here to make sure that it is being uploaded in the same sequence:
html
<input id="uploadfiles" multiple="multiple" name="photos" type="file">
javascript
$("#uploadfiles").change(function(){
imgpreview(document.getElementById('uploadfiles').files);
});
function imgpreview(files) {
var count = 0;
for (var i = 0, f; f = files[i]; i++) {
(function () {
var div = $("<div></div>");
var reader = new FileReader();
$(".display").append(div);
reader.onload = function(e) {
div.append("<img id='photocount" + count + "' src='" + e.target.result + "' style='height:40px;width:auto;'></img>");
count++;
};
reader.readAsDataURL(f);
}());
}
}
It is also available in fiddle here: https://jsfiddle.net/L3d1L9t3/1/
This javascript code here ensures that the images are appearing in sequence. However, the id for the images are still not in order. For example, if 4 images are uploaded, the ids should be photocount0, photocount1, photocount2, photocount3 respectively. But this is not the case when i inspect element on each of the images.
How do i ensure that the "count" is in sequence as well? This is important since i need to match the count to the text field (image description) as well ["image 1" is paired with "image description 1", "image 2" is paired with "image description 2" and so on]
Use URL.createObjectURL(file) instead it's both faster and easier since it's sync - you don't have to encode/decode back and fort to/from base64 then
$("#uploadfiles").change(function() {
// imgpreview(document.getElementById('uploadfiles').files); <-- not needed
imgpreview(this.files)
})
function imgpreview(files) {
var url, div, len = files.length
var $display = $(".display")
for (var i = 0; i < len; i++) {
url = URL.createObjectURL(files[i])
div = $('<div>')
$display.append(div)
div.append("<img id='photocount" + i + "' src=" + url + " style='height:40px;width:auto'>")
}
}

Categories