CristianHG CristianHG - 7 months ago 34
Javascript Question

Tell AJAX that the request was completed without canceling file upload

I am sorry if the title was misleading, there is no easy way to abridge my issue in a title. I am currently experimenting with AJAX file uploads; I had done standard file uploads before but now I am trying to make my application's interface a bit better by adding a progress bar that tracks the file upload progress and notifies the user when the video finishes uploading, finishes processing and is put into the database.

It is working mostly fine, except for the fact that Ajax is currently working for my script to finish execution. Basically, what's happening is that most of the calls are made from a single file called

uploadfiles.php
. I am using a while loop with
feof
and logging the progress to a file, which is then supposed to be fetched by another AJAX that loops until the file tells it that the request has changed/completed.

But, again, the progress bar doesn't move as for some reason the first AJAX request is waiting until
uploadfiles.php
fully finishes executing (Which would be when the file finishes uploading, processing and gets moved, and would make the progress bar pointless), and due to that doesn't let the next AJAX request that retrieves the log file contents. I have tried the following:


  • Did
    ob_start(); and ob_end_flush()
    just after the file is moved from PHP's tmp folder to my custom tmp folder

  • Did
    flush()
    , which I suppose is the exact same thing as the point above

  • Echoed something random and flushed again, hoping something would happen



I have also added
ignore_user_abort()
to ensure the request doesn't get aborted if the user leaves the page/the request ends or gets aborted.

**Here's the JS code:

function uploadFiles()
{
var data2 = new FormData($("#specialform")[0]);

timestamp = new Date().getUTCMilliseconds();
timestamp = timestamp.toString();

data2.append('outputId', timestamp);

console.log(data2);

$.ajax({
type : "POST",
url : "actions/uploadfiles.php",
data : data2,
processData : false,
contentType : false,
success: function(data)
{
alert('Finished first request');
getLog();
},
error: function (xhr, ajaxOptions, thrownError)
{
alert(xhr.responseText);
alert(thrownError);
},
xhr: function ()
{
var xhr = new window.XMLHttpRequest();

xhr.addEventListener("progress", function (evt)
{
if (evt.lengthComputable)
{
var percentComplete = evt.loaded / evt.total;
console.log(percentComplete);
var percentComplete = percentComplete * 100;

$("#progressBar").css({ width : percentComplete + '%' });

if ( percentComplete >= 100 )
xhr.abort();
}
else
console.log('unable to complete');
}, false);

return xhr;
},
})
.fail(function(data)
{
console.log(data);
});

//return {'error' : 'No files', 'result' : null};
}

function getLog()
{
if ( finished == false )
{
console.log("logs/" + timestamp);

$.ajax({
type : "GET",
url : "logs/" + timestamp,
processData : false,
contentType : false,
success: function(data)
{
if ( data == "processing" && !processed )
{
alert('processing video');

$("#progressBar").css({ 'background-color' : 'yellow' });

processed = true;
}

if ( data == "done" )
{
alert('finished conversion');

$("#progressBar").css({ 'background-color' : 'green' });

finished = true;
}

if ( $.isNumeric(data) )
$("#progressBar").css({ width : data + '%' });

console.log(data);
}
});

setTimeout(getLog, 1000);
}
}


Here's the PHP code:

<?php

require '../Init.php';

define('TMP_PATH', PATH.'/tmp');
define('V_STORE', PATH.'/resources/videos');
define('FFMPEG_PATH', 'F:/Webserver/ffmpeg/bin/ffmpeg.exe');

// ...

ob_start();

echo "END";

ob_end_flush();
flush();

// ...

$remote = fopen($_FILES['file']['tmp_name'], 'r');
$local = fopen($filename, 'w');

$read_bytes = 0;

set_time_limit(28800000);
ignore_user_abort(true);

$interval = 0;

while( !feof($remote) )
{
$buffer = fread($remote, 2048);
fwrite($local, $buffer);

$read_bytes += 2048;

$progress = min(100, 100 * $read_bytes / $filesize);

if ( $interval <= 0 )
{
file_put_contents('../logs/'.$logFile, $progress);

$interval = 1000;
}
else
$interval--;
}

// ...

$proc = popen(FFMPEG_PATH.' -i '.$filename.' -b '.$newBitrate.' '.V_STORE.'/'.$video_id.'.mp4', 'r');

while (!feof($proc))
{
file_put_contents('../logs/'.$logFile, fread($proc, 4096));
}

file_put_contents('../logs/'.$logFile, "done");

sleep(5);

unlink('../logs/'.$logFile);


Don't worry about the
Init.php
as (In this case) it is only used for the
PATH
constant

Thanks in advance.

Answer

Are you using PHP file based sessions in your Init.php file (or elsewhere)? By default these files lock until the session is closed effectively only allowing one request to execute at a time.

If so you need to close the session in your upload file using:

session_write_close();