Nonsensical values of loaded and total in AJAX file upload - javascript

I am trying to build a progress bar for multiple files drag and drop upload by combining code from tutorial on multiple files drag and drop uploading and tutorial on progress bar uploading.
Javascript part is:
var dropzone = document.getElementById("dropzone");
function updateProgress(e){
document.getElementById("progress").innerHTML = e.loaded + " of " + e.total;
}
function upload(files){
var formData = new FormData(),
xhr = new XMLHttpRequest(),
x;
for(x = 0; x < files.length; x++){
formData.append("file[]", files[x]);
}
xhr.addEventListener("progress", updateProgress, false);
xhr.open("post", "upload.php");
xhr.send(formData);
}
dropzone.ondrop = function(e){
e.preventDefault();
this.className = "dropzone";
upload(e.dataTransfer.files);
}
dropzone.ondragover = function(){
this.className = "dropzone dragover";
return false;
}
dropzone.ondragleave = function(){
this.className = "dropzone";
return false;
}
And upload.php is simply:
<?php
if(!empty($_FILES["file"]["name"][0]))
foreach($_FILES["file"]["name"] as $position => $name)
move_uploaded_file($_FILES["file"]["tmp_name"][$position], "uploads/".$name);
?>
For start, before making actual progress bar, I just want to show the number of uploaded and total bytes. However, function updateProgress doesn't get called if upload.php echoes nothing and otherwise (e.g. if I add echo "something";) e.loaded and e.total are small numbers of same value, unrelated to file size.
File upload itself works fine, even with large files (few hundred MBs). With large files, I've noticed that function updateProgress is called only once - after the upload is complete.
Why is this event handling behaving like this and how to fix it?

You are setting a progress handler for download, to set one for upload use
xhr.upload.addEventListener("progress", updateProgress, false);

Related

Limitations for controlling files with JavaScript

Please excuse me for not being good at English because I do not live in an English-speaking country.
I am student, and I want to make code for file encryption and download that file with jquery(in client side).
I solved it, but I have some problems.
When I upload file for encrypting file that has big size,
my site is crashed.
(My code will be applied to a simple homepage.)
When I run that process in firefox or icedragon browser, I can see error that 'allocation size overflow'.
Can you tell me why that problem happened?
The code I wrote is below and the reference address is here.
$(function(){
var body = $('body'),
stage = $('#stage'),
back = $('a.back');
/* Step 1 */
$('#step1 .encrypt').click(function(){
body.attr('class', 'encrypt');
// Go to step 2
step(2);
});
$('#step1 .decrypt').click(function(){
body.attr('class', 'decrypt');
step(2);
});
/* Step 2 */
$('#step2 .button').click(function(){
// Trigger the file browser dialog
$(this).parent().find('input').click();
});
// Set up events for the file inputs
var file = null;
$('#step2').on('change', '#encrypt-input', function(e){
// Has a file been selected?
if(e.target.files.length!=1){
alert('Please select a file to encrypt!');
return false;
}
file = e.target.files[0];
/*if(file.size > 1024*1024*50){
alert('Please choose files smaller than 50mb, otherwise you may crash your browser. \nThis WARNING is for you.');
return;
}*/
step(3);
});
$('#step2').on('change', '#decrypt-input', function(e){
if(e.target.files.length!=1){
alert('Please select a file to decrypt!');
return false;
}
file = e.target.files[0];
step(3);
});
/* Step 3 */
$('a.button.process').click(function(){
var input = $(this).parent().find('input[type=password]'),
a = $('#step4 a.download'),
password = input.val();
input.val('');
if(password.length<5){
alert('Please choose a longer password!');
return;
}
// The HTML5 FileReader object will allow us to read the
// contents of the selected file.
var reader = new FileReader();
if(body.hasClass('encrypt')){
// Encrypt the file!
reader.onload = function(e){
// Use the CryptoJS library and the AES cypher to encrypt the
// contents of the file, held in e.target.result, with the password
var encrypted = CryptoJS.AES.encrypt(e.target.result, password);
var encryptedFileArray = [encrypted];
var blob = 'Blob Data';
var fileName = file.name + '.encrypted';
if(window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(blob, fileName);
} else {
var blobEncrypted = new Blob(encryptedFileArray, { type: 'application/octet-stream' });
var blobUrl = URL.createObjectURL(blobEncrypted);
$(a).attr({
'download': file.name + '.encrypted',
'href': blobUrl
});
}
// The download attribute will cause the contents of the href
// attribute to be downloaded when clicked. The download attribute
// also holds the name of the file that is offered for download.
step(4);
};
// This will encode the contents of the file into a data-uri.
// It will trigger the onload handler above, with the result
reader.readAsDataURL(file);
}
else {
// Decrypt it!
reader.onload = function(e){
var decrypted = CryptoJS.AES.decrypt(e.target.result, password).toString(CryptoJS.enc.Latin1);
if(!/^data:/.test(decrypted)){
alert("Invalid pass phrase or file! Please try again.");
return false;
}
var url = decrypted;
var userAgent = navigator.userAgent;
var filename = file.name.replace('.encrypted','');
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = function () {
$(a).attr({
'download': filename,
'href': window.URL.createObjectURL(xhr.response) // xhr.response is a blob
});
};
xhr.open('GET', url);
xhr.send();
step(4);
};
reader.readAsText(file);
}
});
/* The back button */
back.click(function(){
// Reinitialize the hidden file inputs,
// so that they don't hold the selection
// from last time
$('#step2 input[type=file]').replaceWith(function(){
return $(this).clone();
});
step(1);
});
// Helper function that moves the viewport to the correct step div
function step(i){
if(i == 1){
back.fadeOut();
}
else{
back.fadeIn();
}
// Move the #stage div. Changing the top property will trigger
// a css transition on the element. i-1 because we want the
// steps to start from 1:
stage.css('top',(-(i-1)*100)+'%');
}
});
xhr.open('GET', url);
you are using GET method. maybe is better using POST instead.
what is your server side language? is it PHP?
if that is PHP, should be better check the file upload size in "php.ini" file.

Can i do a scheduled task programming with javascript?

I want to do a sheduled task for every day.I have multiple servers and i want to automate the upload of html file to my other servers.In this case i have on the same folder my html and my script.js.Im currently using ajax to upload the html file but i want to do that without interference.Here is my javascript.
$(function(){
$("#drop-box").click(function(){
$("#upl").click();
});
// To prevent Browsers from opening the file when its dragged and dropped on to the page
$(document).on('drop dragover', function (e) {
e.preventDefault();
});
// Add events
$('input[type=file]').on('change', fileUpload);
// File uploader function
function fileUpload(event){
$("#drop-box").html("<p>"+event.target.value+" uploading...</p>");
files = event.target.files;
var data = new FormData();
var error = 0;
for (var i = 0; i < files.length; i++) {
var file = files[i];
console.log(file.type);
if(!file.type.match('html.*')) {
$("#drop-box").html("<p> Html only. Select another file</p>");
error = 1;
}else if(file.size > 1048576){
$("#drop-box").html("<p> Too large Payload. Select another file</p>");
error = 1;
}else{
data.append('html', file, file.name);
}
}
if(!error){
var xhr = new XMLHttpRequest();
xhr.open('POST', 'upload.php', true);
xhr.send(data);
xhr.onload = function () {
if (xhr.status === 200) {
$("#drop-box").html("<p> File Uploaded. Select more files</p>");
} else {
$("#drop-box").html("<p> Error in upload, try again.</p>");
}
};
}
}
This script work fine with my server side, but i want to be able to perform this html upload every day.Is this possible ? what about SetInterval and SetTimeout ?
You can schedule tasks with JavaScript so that they are executed in specific intervals. But you can not upload files from the local system to the server:
JavaScript can't start file transfers on it's own due to security reasons and always needs manual user interaction to do this.
The reason why your above script works is because fileUpload() is orginally triggered by the user. As soon as you use timeout() or interval(), the browser detects that the operation was not triggered by the user and won't allow you to upload user data.

Managing Progress Bars for Multiple File Upload

So I am using a ajax to upload multiple files. Everything seems to be working like a charm... I just can't get to make my progress bars to work ...
Any help would be really appreciated. Thanks.
var images = document.getElementById('images');
for(var i=0;i<images.files.length;i++) {
var formData = new FormData();
var image = images.files[i];
formData.append('image', image);
formData.append('order_id', document.getElementById('order_id').value);
var xmlhttp=new XMLHttpRequest();
xmlhttp.open("POST","/pictures/uploadImage");
xmlhttp.send(formData);
xmlhttp.upload.addEventListener('progress', function(e){
document.getElementById("image_"+i+"_progress").value = Math.ceil(e.loaded/e.total)*100;
}, false);
}
I am basically uploading images individually .. I figured that would help me track the progress bars better ... Perhaps there's another approach.
According to [MDN][1]:
Note: You need to add the event listeners before calling open() on the request. Otherwise the progress events will not fire.
So, combining this knowledge with Engin's answer, you could do like this:
const images = document.getElementById('images');
const completedCount = 0; // added for loadend scenario
const length = images.files.length;
for (let i = 0; i < length; i++) {
const formData = new FormData();
const image = images.files[i];
formData.append('image', image);
formData.append('order_id', document.getElementById('order_id').value);
const xmlhttp = new XMLHttpRequest();
(elId => {
xmlhttp.upload.addEventListener('progress', e => {
document.getElementById('image_' + elId + '_progress').value = Math.ceil(e.loaded / e.total) * 100;
}, false);
})(i); // to unbind i.
// --- added for loadend scenario. ---
xmlhttp.addEventListener('loadend', () => {
completedCount++;
if (completedCount == length) {
// here you should hide your gif animation
}
}, false);
// ---
xmlhttp.open('POST', '/pictures/uploadImage');
xmlhttp.send(formData);
}
UPDATE:
To catch the event when all files are uploaded you may use loadend events. I've updated my code (see comments), I'm not sure this is a correct way though.
[1]: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Monitoring_progress
i did not try but i think it works, because for loop finished before your post and "i"s value equal to images.files.length. sorry for terrible english
try this:
var images = document.getElementById('images');
for(var i=0;i<images.files.length;i++) {
getProgress(images.files[i],i);
}
function getProgress(image,order){
var formData = new FormData();
formData.append('image', image);
formData.append('order_id', document.getElementById('order_id').value);
var xmlhttp=new XMLHttpRequest();
xmlhttp.open("POST","/pictures/uploadImage");
xmlhttp.send(formData);
xmlhttp.upload.addEventListener('progress', function(e){
document.getElementById("image_"+order+"_progress").value = Math.ceil(e.loaded/e.total)*100;
}, false);
}
You can include another function.
function upload(ProgressbarId){
/*
.
.
.
some code
*/
xmlhttp.upload.addEventListener("progress", function(evt){uploadProgress(evt,ProgressbarId);}, false);
}
function uploadProgress(evt,ProgressbarId) {
if (evt.lengthComputable) {
var percentComplete = Math.round(evt.loaded * 100 / evt.total);
set_Progressbar(ProgressbarId,percentComplete);
}
}
}

Upload Image Using AJAX with Progress Indicator

I am trying to upload an image using AJAX. I have the local URL of my image
and I want to pass that image as a file to the web service to upload.
Suppose i have the local file URL as : file:///accounts/1000/shared/camera/IMG_00000322.jpg
Now using AJAX I want to pass this to webservice,
What will be the best way to do this? I also want show the progress while uploading
Using php in server side.
uploadImage : function(myImageUrl,chatId){
var formData = new FormData();
formData.append("chatId", chatId);
formData.append("fileimage", myImageUrl);
$.ajax(
{
type:"POST",
url:"http://suresh.sp.in/butler/public/uploadimage/getimage",
contentType:"image/png",
dataType:"json",
data:formData,
success:function(uploaded){
console.info(uploaded.status);
},
error:function(error){
console.info(error);
}
});
}
I used that snippet on several of my websites, it handles Multiples files upload, drag and drop, and a progress bar.
HTML
You will need a container to drop your batch of files, in our case it will be #output, and a list of files.
JS
First we will push the dataTransfer to jQuery's event and bind the drop event.
$(document).ready(function(){
// We add the dataTransfer special property to our jQuery event
$.event.props.push("dataTransfer");
// We bind events for drag and drop
$('#output').bind({
"dragenter dragexit dragover" : do_nothing,
drop : drop
});
});
// stop event propagation
function do_nothing(evt){
evt.stopPropagation();
evt.preventDefault();
}
Then we build our update progress function
// Progress bar update function
function update_progress(evt,fic) {
var id_tmp=fic.size;
//id_tmp help us keep track of which file is uploaded
//right now it uses the filesize as an ID: script will break if 2 files have the
// same size
if (evt.lengthComputable) {
var percentLoaded = Math.round((evt.loaded / evt.total) * 100);
if (percentLoaded <= 100) {
$('#'+id_tmp+' .percent').css('width', percentLoaded + '%');
$('#'+id_tmp+' .percent').html(percentLoaded + '%');
}
}
}
Last but not least our drop function
function drop(evt){
do_nothing(evt);
var files = evt.dataTransfer.files;
// Checking if there are files
if(files.length>0){
for(var i in files){
// if its really a file
if(files[i].size!=undefined) {
var fic=files[i];
// We add a progress listener
xhr = jQuery.ajaxSettings.xhr();
if(xhr.upload){
xhr.upload.addEventListener('progress', function (e) {
update_progress(e,fic);
},false);
}
provider=function(){ return xhr; };
// We build our FormData object
var fd=new FormData;
fd.append('fic',fic);
// For each file we build and Ajax request
$.ajax({
url:'/path/to/save_fic.php',
type: 'POST',
data: fd,
xhr:provider,
processData:false,
contentType:false,
complete:function(data){
//on complete we set the progress to 100%
$('#'+data.responseText+' .percent').css('width', '100%');
$('#'+data.responseText+' .percent').html('100%');
}
});
// for each file we setup a progress bar
var id_tmp=fic.size;
$('#output').after('<div class="progress_bar loading" id="'+id_tmp+'"><div class="percent">0%</div></div>');
$('#output').addClass('output_on');
// We add our file to the list
$('#output-listing').append('<li>'+files[i].name+'</li>');
}
}
}
}
That method doesn't work in IE9 or below.
Hope it helped!
Source(in french)
Some infos on progress tracking using XMLHttpRequest
Some infos on the datatransfer prop
EDIT:
PHP
From the server side you can handle the files normally using $_FILES etc...
In order to set the progress to 100% in the complete function your php script must echo the filesize.

Is it possible to share an image to facebook using a data URI?

I have several canvases positioned over each other that merge into one as data URI. Everything works fine and I can get the composite image to show up on the page, but the other funcitonality I require is to create the URI and then share to facebook. I wanted to try to do this without sending to the server and do it all client side.
the code isn't necessary to the problem but if you want to see it
<ul class="button-group even-2">
<li><span id='merge-canvas' class="button expand">Save Image</span></li>
<li><span id='share-facebook' class="button expand facebook" >Share</span></li>
</ul>
with the javascript looking like this.
// DROPBOX AND FILE READER
function noopHandler(evt) {
evt.stopPropagation();
evt.preventDefault();
}
function drop(evt) {
evt.stopPropagation();
evt.preventDefault();
var files = evt.dataTransfer.files;
var count = files.length;
// Only call the handler if 1 or more files was dropped.
if (count > 0) {
}
handleFiles(files);
}
function handleFiles(files) {
var file = files[0];
document.getElementById("droplabel").innerHTML = "Processing " + file.name;
var reader = new FileReader();
// init the reader event handlers
reader.onloadend = handleReaderLoadEnd;
// begin the read operation
reader.readAsDataURL(file);
}
function handleReaderLoadEnd(evt) {
// basically clears and redraws the face canvas on load of the users image
document.getElementById("droplabel").innerHTML = "Picture Added Successfully!";
var $canvas = $('canvas');
ctx = $canvas[0].getContext('2d');
face = new Image();
face.onload = function() {
ctx.drawImage(face, 0, 0, 500, (face.height/face.width) * 500);
}
face.src = evt.target.result;
return face;
}
function initializeDropbox() {
var dropbox = document.getElementById("dropbox")
// adds different events for the dropbox and points to the relevant function
dropbox.addEventListener("dragenter", noopHandler, false);
dropbox.addEventListener("dragexit", noopHandler, false);
dropbox.addEventListener("dragover", noopHandler, false);
dropbox.addEventListener("drop", drop, false);
}
which produces a really really long data URI!
Any ideas to accomplish the share?
You can post an image via URL or multipart/form-data in the source parameter:
https://developers.facebook.com/docs/graph-api/reference/v2.1/user/photos
/* make the API call */
FB.api(
"/me/photos",
"POST",
{
"source": "{image-data}"
},
function (response) {
if (response && !response.error) {
/* handle the result */
}
}
);

Categories

Resources