I have a multi-file input field:
<input type="file" class="image_file" multiple>
I am using FileReader to show previews of images whilst they are being uploaded.
I now also want to show a progress bar on each individual image whilst it is being uploaded. Here is what I have tried:
$('.image_file').change(function() {
var input = $(this);
var files = this.files;
var total = files.length;
var url = input.attr('data-url');
for (var i = 0; i < total; i++) {
var formData = new FormData();
var file = files[i];
formData.append('image_file', file);
var reader = new FileReader();
reader.onload = function(e) {
var container = $('.photos .photo:not(.active):first');
if (container.length) {
container.css('background-image', 'url(' + e.target.result + ')').addClass('active uploading');
}
};
reader.readAsDataURL(file);
$.ajax({
type: 'post',
url: url,
data: formData,
cache: false,
processData: false,
contentType: false,
xhr: function() {
var myXhr = $.ajaxSettings.xhr();
var progressElem = container.find('progress');
if (myXhr.upload) {
myXhr.upload.addEventListener('progress', function(e) {
if (e.lengthComputable) {
progressElem.attr({
value: e.loaded,
max: e.total
});
}
}, false);
}
return myXhr;
},
success: function(result) {
if (result.status == true) {
$('.success-message').show();
}
else {
alert('There was an error uploading your file.);
}
}
});
}
});
The issue I am having is on this line in the xhr function:
var progressElem = container.find('progress');
The image preview appears but the AJAX upload isn't working. No errors are shown in the console either. I think because var container was set within the reader.onload function, the xhr function doesn't have access to it.
If I move that var outside of the function, the image upload works but only one image preview and one progress bar is shown.
Does anybody know the correct way to do this?
The problem is that there is a single xhr that is created and deleted when the for loop runs. The previous xhr are destroyed once the code finishes so it will never run.
The way I got round this was to not use jQuery and/or create a new xmlhttprequest for each for loop.
var array = []; //ADDED HERE
$('.image_file').change(function() {
var input = $(this);
var files = this.files;
var total = files.length;
var url = input.attr('data-url');
for (var i = 0; i < total; i++) {
var formData = new FormData();
var file = files[i];
formData.append('image_file', file);
var reader = new FileReader();
reader.onload = function(e) {
var container = $('.photos .photo:not(.active):first');
if (container.length) {
container.css('background-image', 'url(' + e.target.result + ')').addClass('active uploading');
}
};
reader.readAsDataURL(file);
array[array.Length] = $.ajax({ //ADDED HERE
type: 'post',
url: url,
data: formData,
cache: false,
processData: false,
contentType: false,
xhr: function() {
var myXhr = $.ajaxSettings.xhr();
var progressElem = container.find('progress');
if (myXhr.upload) {
myXhr.upload.addEventListener('progress', function(e) {
if (e.lengthComputable) {
progressElem.attr({
value: e.loaded,
max: e.total
});
}
}, false);
}
return myXhr;
},
success: function(result) {
if (result.status == true) {
$('.success-message').show();
} else {
alert('There was an error uploading your file.);
}
}
});
}
});
I need to emphasis that I haven't looked through your code completely but hopefully this will steer you in the right direction.
Looking at your question description, I assume:
Image Preview works since you mentioned "The image preview appears"
Image uploads since you mentioned "If I move that var outside of the function, the image upload works..."
Where is the problem then?
The problem is your variable container is not accessible inside xhr() function as you mentioned already.
What is the solution?
There can be many possible solutions for you problem, but I think moving the ajax request block inside reader.onload is better idea since, the variable container will be accessible to child function and it will be called only if vaild file is being uploaded.
$('.image_file').change(function() {
var input = $(this);
var files = this.files;
var total = files.length;
var url = input.attr('data-url');
for (var i = 0; i < total; i++) {
var formData = new FormData();
var file = files[i];
formData.append('image_file', file);
var reader = new FileReader();
reader.onload = function(e) {
var container = $('.photos .photo:not(.active):first');
if (container.length) {
var ajaxFunction = function() {
var myXhr = $.ajaxSettings.xhr();
var progressElem = this.find('progress');
if (myXhr.upload) {
myXhr.upload.addEventListener('progress', function(e) {
if (e.lengthComputable) {
progressElem.attr({
value: e.loaded,
max: e.total
});
}
}, false);
}
return myXhr;
};
container.css('background-image', 'url(' + e.target.result + ')').addClass('active uploading');
$.ajax({
type: 'post',
url: url,
data: formData,
cache: false,
processData: false,
contentType: false,
xhr: ajaxFunction.bind(container),
success: function(result) {
if (result.status == true) {
$('.success-message').show();
} else {
alert('There was an error uploading your file.');
}
}
});
}
};
reader.readAsDataURL(file);
}
});
.photo {
display: none;
height: 200px;
width: 200px;
float: left;
}
.active {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="file" class="image_file" multiple data-url="http://httpbin.org/post">
<div class="photos">
<div class="photo">
<progress></progress>
</div>
<div class="photo">
<progress></progress>
</div>
</div>
Updated: used bind() function to pass current value of the variable container to the ajaxFunction()
Related
I'm having a difficult time trying to get the below code to work in IE. The code works as expected in Firefox, Chrome, and Edge; but not in IE. I would ignore it not working in IE, but it's the default browser used at work.
The code is written to upload multiple files into a specific SharePoint document library. I got the code from this post https://social.msdn.microsoft.com/Forums/office/en-US/bb590f35-da1b-4905-baa0-fb85a275abf6/multiple-files-upload-in-document-library-using-javascript-object-model?forum=appsforsharepoint. It's the last post, and it does work great in the mentioned browsers. Any suggestions on how to get it to work in IE will greatly be appreciated. Thank you in advance.
Script is below:
jQuery(document).ready(function() {
fileInput = $("#getFile");
SP.SOD.executeFunc('sp.js', 'SP.ClientContext', registerClick);
});
function registerClick() {
//Register File Upload Click Event
jQuery("#addFileButton").on('click', readFile);
}
var arrayBuffer;
function readFile() {
//Get File Input Control and read th file name
var element = document.getElementById("getFile");
var fileCount = element.files.length;
var filesUploaded = 0;
for (var i = 0; i < fileCount; i++) {
let file = element.files[i];
var reader = new FileReader();
reader._NAME = element.files[i].name
reader.onload = function(e) {
let fileactualName = e.target._NAME;
uploadFile(e.target.result, fileactualName);
}
reader.onerror = function(e) {
alert(e.target.error);
}
reader.readAsArrayBuffer(file);
}
}
function uploadFile(arrayBuffer, fileName) {
//Get Client Context,Web and List object.
var clientContext = new SP.ClientContext();
var oWeb = clientContext.get_web();
var oList = oWeb.get_lists().getByTitle('Comms Shared Files');
//Convert the file contents into base64 data
var bytes = new Uint8Array(arrayBuffer);
var i, length, out = '';
for (i = 0, length = bytes.length; i < length; i += 1) {
out += String.fromCharCode(bytes[i]);
}
var base64 = btoa(out);
//Create FileCreationInformation object using the read file data
var createInfo = new SP.FileCreationInformation();
createInfo.set_content(base64);
createInfo.set_url(fileName);
//Add the file to the library
var uploadedDocument = oList.get_rootFolder().get_files().add(createInfo)
//Load client context and execcute the batch
clientContext.load(uploadedDocument);
clientContext.executeQueryAsync(QuerySuccess, QueryFailure);
}
function QuerySuccess() {
alert('File Uploaded Successfully.');
}
function QueryFailure(sender, args) {
console.log('Request failed with error message - ' + args.get_message());
}
In SharePoint 2010, we can use SharePoint designer to open the v4.master(defualt), and add "IE=11" in "X-UA-Compatible".
<meta http-equiv="X-UA-Compatible" content="IE=8,IE=11"/>
In SharePoint 2013/2016/2019/online, we can use REST API to upload the files to document library with jQuery code.
<input id="inputFile" type="file" multiple="multiple"/>
<input id="uploadDocumentButton" type="Button" value="Upload Document">
<script src="https://code.jquery.com/jquery-1.12.4.min.js" type="text/javascript"></script>
<script type="text/javascript">
var libraryTitle="DL";
$(function(){
$("#uploadDocumentButton").click(function () {
if (document.getElementById("inputFile").files.length === 0) {
alert("Select a file!");
return;
}
for(var i = 0; i < document.getElementById("inputFile").files.length; i++){
var file = document.getElementById("inputFile").files[i];
uploadFileSync(libraryTitle, file.name, file);
}
alert("upload complete.");
});
});
function uploadFileSync(folderUrl, filename, file){
var reader = new FileReader();
reader.onloadend = function(evt){
if (evt.target.readyState == FileReader.DONE){
var buffer = evt.target.result;
var completeUrl =_spPageContextInfo.webAbsoluteUrl
+ "/_api/web/GetFolderByServerRelativeUrl('"+folderUrl+"')/Files/add(url='" + filename + "',overwrite=true)";
$.ajax({
url: completeUrl,
type: "POST",
data: buffer,
async: false,
processData: false,
headers: {
"accept": "application/json;odata=verbose",
"X-RequestDigest": $("#__REQUESTDIGEST").val(),
"content-length": buffer.byteLength
},
complete: function (data) {
//alert("upload complete.");
//console.log(data.responseJSON.d.ServerRelativeUrl);
},
error: function (err) {
alert('failed');
}
});
}
};
reader.readAsArrayBuffer(file);
}
</script>
I've been having a battle with dropzone.js. No matter what setting I change the plugin will only upload one or two of the files dragged into the dropzone.
Interestingly enough though if I step through all the code using debugger points I can see it going through and uploading each file. And they do upload. Every one of them.
Could this be the plugin working faster than the backend? It is getting to the success function each time so this has be utterly confused.
I have tried all the tricks.
I have the paralleUplads and maxFiles set
parallelUploads: 5,
maxFilesize: 5,
maxFiles: 5,
and I have tried setting these in the init section as well for the queue section as well
init: function() {
this.on("queuecomplete", function() {
this.options.autoProcessQueue = false;
});
this.on("processing", function() {
this.options.autoProcessQueue = true;
});
},
Without the added code above my dropzone function looks like this
$(".somediv").dropzone({
url: 'someurl',
async: false,
clickable: false,
sending: function(file, xhr, formData) {
var fileType = file.type;
var form_data = new FormData(file);
fileType = fileType.substring(fileType.indexOf("/") + 1);
formData.append("data", file);
formData.append("documentID", 0);
formData.append("dataTypeCode", fileType);
formData.append("dataDescription", file.name);
formData.append('filepart', form_data)
},
addedfile: function(file) {
var _this = this,
reader = new FileReader();
reader.onload = function(event) {
_this.processQueue()
};
reader.readAsDataURL(file);
},
success: function(data) {
var fileType = data.type;
fileType = fileType.substring(fileType.indexOf("/") + 1);
iconImg(fileType)
console.log('uploaded ' + data)
var statusCode = 'AD'
var text = "'Attachment Added' by " + currentUser.employeeId
statusCodeChange(statusCode, brCode, incidentId, text)
var randomNum = Math.random() * 20
table.row.add({
"dataDescription": data.name,
"dataTypeCode": fileType,
"documentTimeStamp": formatDate(new Date()),
"documentID": randomNum
}).draw(false)
.node();
growl("Attachment Uploaded!", {});
}
});
Hey guys am new to jQuery,How can I change this javascript code into jQuery functional code so that I call it whenever I want at any object
LIKE: $("#profile_img").uploader();
Apparently this code works fine, but the problem I have is I have to populate the code every time I need to upload a file in a different file input upload.
var input = document.getElementById("choosen_feeds_image"),
formdata = false;
if (window.FormData) {
formdata = new FormData();
document.getElementById("feeds_upload_btn").style.display = "none";
}
if (input.addEventListener) {
input.addEventListener("change", function (evt) {
var i = 0, len = this.files.length, img, reader, file;
document.getElementById("response").innerHTML = ""
for (; i < len; i++) {
file = this.files[i];
if (!!file.type.match(/image.*/)) {
if (window.FileReader) {
reader = new FileReader();
reader.onloadend = function (e) {
showUploadedItem(e.target.result);
};
reader.readAsDataURL(file);
}
if (formdata) {
formdata.append("feeds_image", file);
}
if (formdata) {
$.ajax({
url: "member/feeds_image_upload",
type: "POST",
data: formdata,
processData: false,
contentType: false,
success: function (res) {
if (res.length <= 40) {
document.getElementById('feeds_image_response').innerHTML = res;
$("#feeds_image_response").css('display', 'none');
} else {
document.getElementById("response").innerHTML = res;
$("#response").css('display', 'none');
}
}
});
}
} else {
document.getElementById("response").innerHTML = "";
alert("Sorry, You choose unsupported file");
}
}
}), false
};
you can type all inside a function like this
function uploader(){
console.log('myFuntionUploader');
}
and then call the function like this
uploader();
Take a look below code I'm trying to alert the value of i inside the reader.onloadend it's not showing properly, It's showing max value of i, i.e if i add 3 images i alerting only three it should show 0,1 and 3.
$("#files2").change(function()
{
var src=$("#files2").val();
if(src!="")
{
formdata= new FormData();
var numfiles=this.files.length;
var i, file, progress, size;
for(i=0;i<numfiles;i++)
{
//alert(i);
file = this.files[i];
size = this.files[i].size;
name = this.files[i].name;
if (!!file.type.match(/image.*/))
{
if((Math.round(size))<=(1024*1024))
{
var reader = new FileReader();
reader.readAsDataURL(file);
$("#preview").show();
$('#preview').html("");
//<img src="img/remove.png"/>
//style='margin-left: -16px;margin-top: -6px;'
alert(i);
reader.onloadend = function(e)
{
var image = $('<img id="im'+i+'" style="float:left; height: 150px; width: 125px; margin-left: 5px; margin-right: 10px; margin-bottom: 10px;">').attr('src',e.target.result);
var image1 = $('<img onclick="$(this).remove();$("#im1").remove();" style=" margin-left: -25px;margin-top: -1px;z-index: 1;position: absolute; ">').attr('src','img/remove.png');
$(image).appendTo('#preview');
$(image1).appendTo('#preview');
};
formdata.append("files2[]", file);
if(i==(numfiles-1))
{
$(".nextim").click(function(){
$.ajax(
{
url: "upload.php?contactid=<?php echo $id1; ?>&postid=<?php echo $id2; ?>",
type: "POST",
data: formdata,
processData: false,
contentType: false,
success: function(res)
{
if(res!="0")
$("#info").html("Successfully Uploaded");
else
$("#info").html("Error in upload. Retry");
}
});
return false;
});
}
}
else
{
$("#info").html(name+"Size limit exceeded");
$("#preview").hide();
return;
}
}
else
{
$("#info").html(name+"Not image file");
$("#preview").hide();
return;
}
}
}
else
{
$("#info").html("Select an image file");
$("#preview").hide();
return;
}
return false;
});
From what I understand you should be looping through the number of files that are used. Since you have not showed us how variable i is initialized, I tried using the name as an example. Hope this might help you :)
for (var count = 0; count < files.length; count ++) {
(function(file) {
var name = file.name;
var reader = new FileReader();
reader.onload = function(e) {
alert(name );
}
})(files[count]);
}
I am trying to add the page_id and page_slug to the loop so that when an image is uploaded the details page id and slug go to.
Normally I would use this: data: { page_id: page_id, page_slug: page_slug }
But formData is there..
(function () {
var input = document.getElementById("images"),
formdata = false;
var page_id = $('#page_id').val();
var page_slug = $('#page_slug').val();
function showUploadedItem (source) {
var list = document.getElementById("image-list"),
li = document.createElement("li"),
img = document.createElement("img");
img.src = source;
li.appendChild(img);
list.appendChild(li);
}
if (window.FormData) {
formdata = new FormData();
document.getElementById("btn").style.display = "none";
}
input.addEventListener("change", function (evt) {
document.getElementById("response").innerHTML = "Uploading . . ."
var i = 0, len = this.files.length, img, reader, file;
for ( ; i < len; i++ ) {
file = this.files[i];
if (!!file.type.match(/image.*/)) {
if ( window.FileReader ) {
reader = new FileReader();
reader.onloadend = function (e) {
showUploadedItem(e.target.result, file.fileName);
};
reader.readAsDataURL(file);
}
if (formdata) {
formdata.append("images[]", file);
}
}
}
if (formdata) {
$.ajax({
url: "admin/pages/upload/",
type: "POST",
data: formdata,
processData: false,
contentType: false,
success: function (res) {
document.getElementById("response").innerHTML = res;
}
});
}
}, false);
}());
I do not think you can manipulate the FormData object directly inside the $.ajax or even $.ajaxSetup functions, so adding two calls to append in the following code block should include the required parameters.
if (window.FormData) {
formdata = new FormData();
formdata.append('page_id', page_id);
formdata.append('page_slug', page_slug);
document.getElementById("btn").style.display = "none";
}
The page_id and page_slug values are unique to so do not need appending in the loop; just once after creating the FormData object.
Please also see Using FormData objects for more examples.