How to send curl upload progress to ajax to be displayed - javascript

What I am trying to do is upload the file/files information to upload.php using ajax, then uploading the same information again to a remote server by curl to remoteUpload.php. Finally in remoteUpload.php file I perform the actual upload of the file/files.
When doing the first step -> upload the file/files information to upload.php I display a progress bar of this step using ajax.
But the when doing the second step -> upload the same information again to remote server using curl to remoteUpload.php the progress bar is not displayed, and this is my problem.
How to display the progress bar for second step by ajax ?
Javascript:
var upload_btn = document.getElementById('upload_file');
var result = document.getElementById('result');
upload_btn.onclick = function () {
var uploadInput = document.getElementsByName('file[]')[0];
if (uploadInput.files.length > 0) {
console.clear();
var ajax = new XMLHttpRequest();
var inputFileData = formData(uploadInput);
ajax.onreadystatechange = function () {
if (ajax.readyState == 4 && ajax.status == 200) {
var json = JSON.parse(ajax.responseText);
result.innerHTML = json.text;
}
};
ajax.upload.addEventListener('progress', function (e) {
result.innerHTML = Math.round(e.loaded / e.total * 100) + "%";
});
ajax.open("post", "upload.php");
ajax.send(inputFileData);
}
};
function formData(inputFileObj) {
var formData = new FormData;
var inputFile = inputFileObj.files;
if (inputFile.length > 0) {
for (i = 0; i < inputFile.length; i++) {
formData.append(inputFileObj.name, inputFile[i]);
}
}
return formData;
}
PHP: (upload.php)
function progressCallback($dltotal, $dlnow, $ultotal, $ulnow) {
static $last;
$progress = #round($ulnow / $ultotal * 100);
if($last < $progress) echo json_encode(array('text' => $progress));
flush();
$last = $progress;
}
if (strtolower($_SERVER['REQUEST_METHOD']) == 'post' && !empty($_FILES)) {
foreach ($_FILES['file']['tmp_name'] as $index => $tmpFileName) {
if ($_FILES['file']['error'][$index] > 0) {
$text = "A file did not uploaded correctly.";
return false;
}
$ch = curl_init("http://serverfiles/remoteUpload.php");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_NOPROGRESS, false);
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, 'progressCallback');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, array('fileupload' => '#' . $tmpFileName));
$text = curl_exec($ch);
}
}
echo json_encode(array('text' => $text));
exit;
PHP: (remoteUpload.php)
if (move_uploaded_file($_FILES['fileupload']['tmp_name'], "files/" . $_FILES['fileupload']['name']))
echo "The file has been uploaded.";
else
echo "error";

You can save in progressCallback() to $_SESSION your $progress and from js side after "first step" upland complete run setIntevral(/*ajax*/) make ajax requests to server get $_SESSION['progress] and display second progress bar(or load first progress barr till 50% and continue load second 50%) in your form and when it complete call clearInterval()
DETAILED ANSWER
Explanation: we will count progress in following logic. If we have uploaded 5 files at once (as in your case seems uploaded was multi file) then we will divide 100% by 5 and will increase by 25% progress during one file curl submit, for this you need following 5 modifications
1) call somewhere above session_start() in your upload.php if it not done
2) save to session total files count and current processing file index
// Saveing total amount of uploaded files
$_SESSION['UP_total_count'] = count($_FILES['file']);
foreach ($_FILES['file']['tmp_name'] as $index => $tmpFileName) {
...
// Saving current index of uploaded file
$_SESSION['UP_current_index'] = ($index+1);//+1 as it starts from 0
}
3) save in progressCallback function current progress number in 0-100 format
function progressCallback($dltotal, $dlnow, $ultotal, $ulnow) {
...
$_SESSION['UP_current_progress'] = $progress;
}
4) create new getUploadProgress.php and return json encoded progress infromation from session
session_start();
echo json_encode( array(
'total_count' => $_SESSION['UP_total_count'],
'current_index' => $_SESSION['UP_current_index'],
'current_progress' => $_SESSION['UP_current_progress'],
) );
5) add in your ajax.onreadystatechange setInteval function call, and define in your js global variable progressSetInterval
....
var progressSetInterval = null;// Global
ajax.onreadystatechange = function () {
if (ajax.readyState == 4 && ajax.status == 200) {
...
progressSetInterval = setInterval(function(){
$.ajax({
type: "POST",
url: "getUploadProgress.php",
success: function(data){
// Calculating progress based on 100 25 logic explained above
var progressPart = 100 / data['total_count'];
var currProgress =
(data['total_count'] - data['current_index']) * progressPart;
currProgress += (progressPart/100) * data['current_progress'];
// You can display progress somehow, apped to div or show prgoress...
console.log( "Second progress: " + currProgress);
// if currProgress is 100% removing setinterval
if( currProgress >= 100 ){
clearInterval( progressSetInterval );
}
},
dataType: 'json'
});
}, 1000);
}
};
NOTE: during usage of this example code of course there will be needed additional rounds, JS/PHP functions addition, variable adjustments or some logical adjustments for more effectiveness, but basically this is logic of one option which you can use

Related

AJAX Javascript Element not updating

I have an issue with the Internet Explorer/EDGE browser. Basically, I have a script that pulls data from a remote XML file and displays it in page (live example: http://www.oldiesplus.com/ - The Radio info section, top of the page)
The way it works is that every 15 seconds, the XML file is read and the Song Title is updated (that's the scrolling bit). This works perfectly in Google Chrome, under IE/EDGE, however, the script executes (see Console log) but the element is never updated.
The XML file is grabbed using Curl and CURLOPT_FRESH_CONTENT is set to true.
The question being then, why is the element not updating with the new content in IE/EDGE?
Here's some code to help:
sc_conn.inc (PHP):
$ch = curl_init($sc_host . '/admin.cgi?mode=viewxml');
curl_setopt($ch, CURLOPT_PORT, $sc_port);
curl_setopt($ch, CURLOPT_USERAGENT, $useragent);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, $sc_admin.':'.$sc_pass);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FRESH_CONNECT, TRUE);
$curl = curl_exec($ch);
shoutcast.js (JavaScript):
function getStreamData() {
var ajax;
if (window.XMLHttpRequest) {
ajax = new XMLHttpRequest();
} else {
ajax = new ActiveXObject("Microsoft.XMLHTTP");
}
ajax.open('GET','/sc_data.php', true);
ajax.send();
ajax.onreadystatechange = function() {
if (ajax.readyState == 4 && ajax.status == 200) {
var data = ajax.responseText.split("|");
var song = (data[1] == '') ? 'some music.' : data[1];
var cta = (player_state == 0) ? '/new/img/play.png' : '/new/img/pause.png';
if (data[2]) {
document.getElementById('radio-info').innerHTML = '<h2>'+data[2]+'</h2>';
document.getElementById('radio-info').innerHTML += '<p><span class="dj_name">'+data[0]+' is playing</span> '+song+'</p>';
}else{
document.getElementById('radio-info').innerHTML = '<p><span class="dj_name">'+data[0]+' is playing</span> '+song+'</p>';
}
document.getElementById('tunein').src = cta;
console.log("Title Updated!");
}
}
}
The resolution was to circumvent caching of the AJAX request - appending a random number to the end of the call of the PHP script using:
Math.random();

Pull working PHP file (with scripts) into div using JavaScript without iframes

Working within system constraints, I needed a way to put working code from a local .php or .html into a target div without additional libraries, jfiddle, iframes, etc. (jquery was fine)
Here are my failed attempts.
First part of file
This is some page!
<script>$("#fruit").click(function(){Expand01("fruit.php"); return false;});</script>
A pretty good page...
<script>$("#orange").click(function(){Expand01("orange.php"); return false;});</script>
I like this page
<script>$("#tomato").click(function(){Expand01("tomato.php"); return false;});</script>
Later in file (after Expand01 function declared)
<div id="thisdiv"></div>
Attempt 1
<script> function Expand01(targetUrl){
document.getElementById('thisdiv').style.display = "block";
document.getElementById('thisdiv').innerHTML = targetUrl;
document.getElementById('thisdiv').append = '<div id="thatdiv"></div>';
} </script>
Attempt 2
<script> function Expand01(targetUrl){
var myTargetUrl = new XMLHttpRequest();
document.getElementById('thisdiv').style.display = "block";
myTargetUrl.open("GET", targetUrl, true);
myTargetUrl.setRequestHeader("Content-Type","text/plain");
myTargetUrl.send("");
document.getElementById('thisdiv').innerHTML = myTargetUrl.responseText;
document.getElementById('thisdiv').append = '<div id="thatdiv"></div>';
} </script>
Attempt 3
<script> function Expand01(targetUrl){
document.getElementById('thisdiv').innerHTML = $.get(targetURL);
} </script>
Attempt 4
<script> function Expand01(targetUrl){
var myFile = getHTTPObject();
myFile.onreadystatechange = function() {
if(request.readyState == 4) {
if(myFile.status == 200 || request.status == 304) {
var targetDiv = document.getElementById('thisdiv');
targetDiv.innerHTML = myFile.responseText;
} else {
alert("Failure");
}
}
}
myFile.open("GET", targetUrl, true);
myFile.send(null);
} </script>
This is the method I use when doing this for ajax applications. It also allows for the usage of $_SESSION[] variables as well as any Javascript or jQuery located in the php file you are pulling into your container.
jQuery:
$.post('pageloader.php', {url: 'path/to/file.php'}, function(data){
var o = $.parseJSON(data);
if(o.status == 1){
$('#yourContainer').html(o.markup);
} else {
alert(o.message);
}
});
PHP: (pageloader.php)
$url = $_POST['url'];
$response = array();
ob_start();
include("markup/" . $url); // Replace or remove '"markup/" . ' depending on file locations
$response['markup'] = ob_get_clean();
if($response['markup']){
$response['status'] = 1;
} else {
$response['status'] = 0;
$response['message'] = 'There was an issue loading the page.';
}
echo json_encode($response);
Hope this helps!

Delaying browser Ajax output (LongPolling)

I'm trying to complete a connection using Long Polling, where the browser sends a request to the server and to be awaiting a response. To prevent this door is infinitely open, I created a routine that every 10 seconds the server sends an empty response to the browser, stating that there was nothing yet.
It's all working perfectly, had no problems related to that.
My problem is that when the user clicks on a link on the page, the browser waits for the answer call for power upgrade, or can take up to 10-sec. This makes it appear that the tool is slow.
Does anyone have any idea how to solve this?
Image:
Image:
Follows the JavaScript function used to make the call:
function loadJSON() {
if(libera) {
var data_file = http + "bibliotecas/longpolling/notificacoes.php";
var data = {};
data.n = long_n;
data.u = userchat;
data.m = msgchat;
data.c = chatUsuario;
http_request.onreadystatechange = function() {
if(http_request.readyState == 4 && http_request.status == 200) {
try {
var jsonObj = JSON.parse(http_request.responseText);
var qtd = jsonObj.funcao.length;
if(qtd > 0) {
var funcao = "";
for(var key in jsonObj.funcao) {
funcao = jsonObj.funcao[key];
MontarFuncao(eval(funcao),jsonObj.metodo[key]);
}
}
}
catch (e) {
//alert('Erro - '+ http_request.responseText);
}
loadJSON();
}
}
var string = JSON.stringify(data);
http_request.open("POST", data_file, true);
http_request.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
http_request.setRequestHeader("Content-length", string.length);
http_request.setRequestHeader("Connection", "close");
http_request.send(string);
return;
}
}
Follows the PHP function responsible for staying open expecting some changes in the database:
ob_start();
$json = json_decode(file_get_contents(`php://input`));
while($x < 5) {
if(time() >= (15 + $_SERVER['REQUEST_TIME']) || connection_aborted()) {
echo str_pad(NULL,1);
die(json_encode(array()));
flush();
ob_flush();
break;
}
//Query DB
if(count($retorno) > 0) {
flush();
ob_flush();
echo json_encode($retorno);
exit;
}
else {
flush();
sleep(2);
$x++;
}
}

Check the status of a file move by comparing directory sizes

i'm trying to use PHP to move some files, as it does that I have a script which will compare the source location with the destination location and work out the difference.
It all starts with a button like so:
<input type="button" class="upload_button" onClick="upload_images('drive_<?=$x["letter"]?>');start_upload_progress()" value="<?=$x["name"]?>"/>
This then starts these two scripts:
function upload_images(x) {
var url = "http://localhost:1234/ppa/php/process_upload.php?x="+x
doc("upload_iframe").src = url;
}
First of all it will change a hidden iframe url to the PHP script and send over the drive letter.
function start_upload_progress() {
upload_progress("http://localhost:1234/ppa/php/upload_progress.php",function() {
doc("upload_bar").innerHTML = this;
if(this != 100) {
//doc("upload_bar").innerHTML = x;
setTimeout(start_upload_progress(),1000);
}
});
}
Next the above script will start, the console.log is simply for me to see if it's working, although I do not see "start" in my console. This function uses a callback, the below code simply loads a PHP script and returns the result. If the result is less than 100 then the call is made again every couple of seconds.
function upload_progress(url, callback) {
var http = getHTTPObject();
http.onreadystatechange = function() {
if (http.readyState == 4 && http.status == 200) {
if (http.responseText == "true") {
//window.location.replace("http://localhost:1234/ppa/rotate.php");
}
callback.call(http.responseText);
}
};
http.open("GET", url, true);
http.send();
}
Below is the PHP script, this was working when I set the $_SESSION['tmp'] variable manually.
<?php
session_start();
//If session has been created then ready to start progress bar
if (isset($_SESSION['tmp'])) {
if ($_SESSION['tmp'] != "true") {
$session_date = str_replace("/","",$_SESSION['session_date']);
$tmp = explode("_", $_SESSION['tmp']);
$upload_number = $tmp[1];
$drive = $tmp[0];
//get paths
$destination = $_SESSION['ROOT_PATH']."data/images/".str_replace("/","",$_SESSION['session_date'])."/".$upload_number."/";
$source = $drive.":/DCIM/";
if (is_dir($destination) && is_dir($source)) {
$source_size = foldersize($source);
$destination_size = foldersize($destination);
echo "Percentage: ".floor(($destination_size / $source_size * 100));
}
} else {
echo "done: ".$_SESSION['tmp'];
}
}
function foldersize($path) {
$total_size = 0;
$files = scandir($path);
$cleanPath = rtrim($path, '/'). '/';
foreach($files as $t) {
if ($t<>"." && $t<>"..") {
$currentFile = $cleanPath . $t;
if (is_dir($currentFile)) {
$size = foldersize($currentFile);
$total_size += $size;
}
else {
$size = filesize($currentFile);
$total_size += $size;
}
}
}
return $total_size;
}
?>
The $_SESSION['tmp'] variable is created just before the files begin to move, it seems like I can't access this session variable until the code has finish executing...
It is created here, at the top of the file moving script:
if (isset($drive)) {
$x = explode("_", $drive);
$x = $x[1];
//Check if connected file exists
if(file_exists($x.":/connected.txt")) {
$file = file_get_contents($x.":/connected.txt");
if ($file != false) {
$file_code = split(":", $file);
//Check if the file has the correct pass code
if($file_code[0] == $code) {
$_SESSION['tmp'] = $x."_".$drive;
When the files have finished moving the variable is set to "true", which seems to be the only value I can get...
Any ideas why my "start_upload_progress" function is not working? The console.log isn't running and I don't think the $_SESSION['tmp'] variable is setting until the file moving script has finished.
Edit 2:
I have got the script running, it compares the source and the destination and updated the innerHTML of the div with the difference. Only issue is that it only seems to work when I move the files from one place to the other manually.
The upload_progress script seems to freeze whilst the files are being moved, it should run at the same time to check the progress.
I have watched the files disappear from the source folder as well but all at the same time, shouldn't they move one by one as the scripting is only moving them one by one...

Video File Upload Progress Bar with Javascript and PHP

I want to upload a video file using PHP and show the progress of the upload via an Progress Bar. But this is more difficult like i thought and i tried to put the pieces ive found together but unfortunately i didnt found a working piece of code that has the needed php, ajax and html code all together, so ive tried to put different pieces together.
My Code functions nearly completely. The only thing is, that the current process of the file upload, which i've got in percent, is loaded by my javascript only AFTER the process has ended, and not from the beginning.
Here is my PHP Code:
function file_get_size($file) {
//open file
$fh = fopen($file, "r");
//declare some variables
$size = "0";
$char = "";
//set file pointer to 0; I'm a little bit paranoid, you can remove this
fseek($fh, 0, SEEK_SET);
//set multiplicator to zero
$count = 0;
while (true) {
//jump 1 MB forward in file
fseek($fh, 1048576, SEEK_CUR);
//check if we actually left the file
if (($char = fgetc($fh)) !== false) {
//if not, go on
$count ++;
} else {
//else jump back where we were before leaving and exit loop
fseek($fh, -1048576, SEEK_CUR);
break;
}
}
//we could make $count jumps, so the file is at least $count * 1.000001 MB large
//1048577 because we jump 1 MB and fgetc goes 1 B forward too
$size = bcmul("1048577", $count);
//now count the last few bytes; they're always less than 1048576 so it's quite fast
$fine = 0;
while(false !== ($char = fgetc($fh))) {
$fine ++;
}
//and add them
$size = bcadd($size, $fine);
fclose($fh);
return $size;
}
$filesize = file_get_size('remote-file');
$remote = fopen('remote-file', 'r');
$local = fopen('local-file', 'w');
$read_bytes = 0;
while(!feof($remote)) {
$buffer = fread($remote, 2048);
fwrite($local, $buffer);
$read_bytes += 2048;
//Use $filesize as calculated earlier to get the progress percentage
$progress = min(100, 100 * $read_bytes / $filesize);
fwrite(fopen('files/upload/progress.txt', 'w'), $progress);
//you'll need some way to send $progress to the browser.
//maybe save it to a file and then let an Ajax call check it?
}
fclose($remote);
fclose($local);
This is my Javascript Code:
function main()
{
var pathOfFileToRead = "files/upload/progress.txt";
var contentsOfFileAsString = FileHelper.readStringFromFileAtPath
(
pathOfFileToRead
);
document.body.innerHTML = contentsOfFileAsString;
}
function FileHelper()
{}
{
FileHelper.readStringFromFileAtPath = function(pathOfFileToReadFrom)
{
var request = new XMLHttpRequest();
request.open("GET", pathOfFileToReadFrom, false);
request.send(null);
var returnValue = request.responseText;
return returnValue;
}
}
main();
function progressBarSim(al) {
var bar = document.getElementById('bar-fill');
var status = document.getElementById('status');
status.innerHTML = al+"%";
bar.value = al;
al++;
var sim = setTimeout("progressBarSim("+al+")",1000);
if(al == 100){
status.innerHTML = "100%";
bar.value = 100;
clearTimeout(sim);
var finalMessage = document.getElementById('finalMessage');
finalMessage.innerHTML = "Process is complete";
}
}
var amountLoaded = 0;
progressBarSim(amountLoaded);
The Progressbar does currently work over an Timer, because the main() function doesnt read the content of the "progress.txt" from the beginning but only at the end. so i would like to have some help to combine progressBarSim with main().
*Edit: * I have found a working piece of code: http://www.it-gecko.de/html5-file-upload-fortschrittanzeige-progressbar.html and am using that now.
Here is a ajax function for modern browsers:
//url,callback,type,FormData,uploadFunc,downloadFunc
function ajax(a,b,e,d,f,g,c){
c=new XMLHttpRequest;
!f||(c.upload.onprogress=f);
!g||(c.onprogress=g);
c.onload=b;
c.open(e||'get',a);
c.send(d||null)
}
more about this function https://stackoverflow.com/a/18309057/2450730
here is the html
<form><input type="file" name="file"><input type="submit" value="GO"></form>
<canvas width="64" height="64"></canvas>
<canvas width="64" height="64"></canvas>
<pre></pre>
you can add more fields inside the form and you don't need to change anything in the javascript functions. it always sends the whole form.
this is the code to make this ajax function work
var canvas,pre;
window.onload=function(){
canvas=document.getElementsByTagName('canvas');
pre=document.getElementsByTagName('pre')[0];
document.forms[0].onsubmit=function(e){
e.preventDefault();
ajax('upload.php',rdy,'post',new FormData(this),progressup,progressdown)
}
}
function progressup(e){
animate(e.loaded/e.total,canvas[0],'rgba(127,227,127,0.3)')
}
function progressdown(e){
animate(e.loaded/e.total,canvas[1],'rgba(227,127,127,0.3)')
}
function rdy(e){
pre.textContent=this.response;
}
this is the animation that moves the circular canvas progress bar
function animate(p,C,K){
var c=C.getContext("2d"),
x=C.width/2,
r=x-(x/4),
s=(-90/180)*Math.PI,
p=p||0,
e=(((p*360|0)-90)/180)*Math.PI;
c.clearRect(0,0,C.width,C.height);
c.fillStyle=K;
c.textAlign='center';
c.font='bold '+(x/2)+'px Arial';
c.fillText(p*100|0,x,x+(x/5));
c.beginPath();
c.arc(x,x,r,s,e);
c.lineWidth=x/2;
c.strokeStyle=K;
c.stroke();
}
you can extend this function with some nice bounce effect on initializzation or on progresschange.
http://jsfiddle.net/vL7Mp/2/
to test i would just simply use a upload.php file like that
<?php
print_r(array('file'=>$_FILE,'post'=>$_POST));
?>
test it with chrome first... then apply the necessary changes to use it with older browsers... anyway this code should work with all the newest browsers now.
i understand that this functions are not simple to understand so ...
if you have any questions just ask.
maybe there are some syntax error or something is missing... because i just copied the whole functions and applied some changes on the fly.
some other useful functions:
display a readable filesize:
https://stackoverflow.com/a/20463021/2450730
convert MS to time string or a time string to MS
function ms2TimeString(a){
var ms=a%1e3>>0,s=a/1e3%60>>0,m=a/6e4%60>>0,h=a/36e5%24>>0;
return (h<10?'0'+h:h)+':'+(m<10?'0'+m:m)+':'+(s<10?'0'+s:s)+'.'+(ms<100?(ms<10?'00'+ms:'0'+ms):ms);
}
function timeString2ms(a){
var ms=0,b;
a=a.split('.');
!a[1]||(ms+=a[1]*1);
a=a[0].split(':'),b=a.length;
ms+=(b==3?a[0]*3600+a[1]*60+a[2]*1:b==2?a[0]*60+a[1]*1:s=a[0]*1)*1e3;
return ms
}
A simple PAD LEFT function
function padL(a,b,c){//string,length=2,char=0
return (new Array(b||2).join(c||0)+a).slice(-b);
}
ps.i'm also working on a conversion progress bar. if you are intrested i can show you my progress.

Categories