admin管理员组

文章数量:1287125

I can't figure out how to push the progress out every time it updates using php. For the sake of clarity, I will write an example.

jQuery:

function uploadMovieDownload(link){
    $.post("php/downloadmovie.php", { source:link }, function(json){ console.log(json); });
}

uploadMovieDownload(url);

PHP (php/downloadmovie.php):

session_start();
ob_start();
date_default_timezone_set("Europe/Bucharest");
ini_set('display_errors',true);

require_once(dirname(__FILE__)."/functions.php");


$url = $_POST['source'];
$headers = getHeaders($url);
$url = $headers['url'];
$path = dirname(__FILE__)."/temp/test.mp4";

$fp = fopen ($path, 'w+');
$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, false );
curl_setopt( $ch, CURLOPT_PROGRESSFUNCTION, 'progress' );
curl_setopt( $ch, CURLOPT_NOPROGRESS, false );
curl_setopt( $ch, CURLOPT_BINARYTRANSFER, true );
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 100 );
curl_setopt( $ch, CURLOPT_FILE, $fp );
curl_exec( $ch );
curl_close( $ch );
fclose( $fp );


function progress($resource,$download_size, $downloaded, $upload_size, $uploaded){
    if($download_size > 0) echo $downloaded / $download_size  * 100;
    ob_flush();
    flush();
}

echo "Done";
ob_flush();
flush();

The problem I have is that it returns the progress after it pletes, it isn't pushing it while downloading. Thanks in advance if you have any sugestions.

I can't figure out how to push the progress out every time it updates using php. For the sake of clarity, I will write an example.

jQuery:

function uploadMovieDownload(link){
    $.post("php/downloadmovie.php", { source:link }, function(json){ console.log(json); });
}

uploadMovieDownload(url);

PHP (php/downloadmovie.php):

session_start();
ob_start();
date_default_timezone_set("Europe/Bucharest");
ini_set('display_errors',true);

require_once(dirname(__FILE__)."/functions.php");


$url = $_POST['source'];
$headers = getHeaders($url);
$url = $headers['url'];
$path = dirname(__FILE__)."/temp/test.mp4";

$fp = fopen ($path, 'w+');
$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, false );
curl_setopt( $ch, CURLOPT_PROGRESSFUNCTION, 'progress' );
curl_setopt( $ch, CURLOPT_NOPROGRESS, false );
curl_setopt( $ch, CURLOPT_BINARYTRANSFER, true );
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 100 );
curl_setopt( $ch, CURLOPT_FILE, $fp );
curl_exec( $ch );
curl_close( $ch );
fclose( $fp );


function progress($resource,$download_size, $downloaded, $upload_size, $uploaded){
    if($download_size > 0) echo $downloaded / $download_size  * 100;
    ob_flush();
    flush();
}

echo "Done";
ob_flush();
flush();

The problem I have is that it returns the progress after it pletes, it isn't pushing it while downloading. Thanks in advance if you have any sugestions.

Share Improve this question edited Dec 31, 2016 at 21:42 LF-DevJourney 28.6k30 gold badges165 silver badges316 bronze badges asked Jan 30, 2015 at 17:10 d3nm4kd3nm4k 1,0231 gold badge8 silver badges12 bronze badges 2
  • Hi have you got the solution for this issue.... – Ashish Commented Dec 30, 2016 at 13:02
  • a few ideas: 1. load balancers can inadvertently save up gradual responses. 2. you need to ship a certain # of bytes right away to get some browsers to behave right. 3. you need to return HTML with the data methodically wrapped in <script> tags to use a simplistic COMET approach like this. 4. i would consider re-implementing using a temp file to store the percentage and pinging another simple script to fetch that value from the file, bypassing the above script. 5. "SSE", aka EventSource is cross-browser and easy to implement in PHP to replace the pinging in #4... – dandavis Commented Dec 30, 2016 at 21:42
Add a ment  | 

4 Answers 4

Reset to default 11 +50

with that method, you'll run in to all kind of caching problems, is php output buffering? you'll have a problem. are you behind a web server, ala nginx/apache/lighthttp/anything? you'll have a problem. is the browser cacheing output? (all mainstream browsers do), you'll have a problem.

i suggest an alternative which will have none of those problems: using $_SESSION to store download percentage, and querying the percentage with XMLHttpRequests (actually querying the percentage over WebSockets would be optimal, lagless, use less bw, etc, but much harder to implement)

downloadmovie.php

<?php 
require_once(dirname(__FILE__)."/functions.php");


$url = $_POST['source'];
$headers = getHeaders($url);
$url = $headers['url'];
//after validation of input
session_start();
$_SESSION['download_percentage']=0.0;//initialize it
session_write_close();
fastcgi_finish_request();//or if you're not using fcgi, some equivalent..

$path = dirname(__FILE__)."/temp/test.mp4";

$fp = fopen ($path, 'w+');
$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, false );
curl_setopt( $ch, CURLOPT_PROGRESSFUNCTION, 'progress' );
curl_setopt( $ch, CURLOPT_NOPROGRESS, false );
curl_setopt( $ch, CURLOPT_BINARYTRANSFER, true );
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 100 );
curl_setopt( $ch, CURLOPT_FILE, $fp );
curl_exec( $ch );
curl_close( $ch );
fclose( $fp );


function progress($resource,$download_size, $downloaded, $upload_size, $uploaded){
    $percentage=$download_size==0? 0.0 : (($downloaded/$download_size)*100);
    session_start();
    $_SESSION['download_percentage']=$percentage;
    session_write_close();
}

getProgress.xhr.php

<?php 
if(""===session_id()){
session_start();
}
echo $_SESSION['download_percentage']??'?';

then monitoring the progress in the browser:

(function checkProgress() {
    "use strict";
    var xhr = new XMLHttpRequest();
    xhr.open("GET", "getProgress.xhr.php");
    xhr.addEventListener("readystatechange", function(ev) {
        var xhr = ev.target;
        if (xhr.readyState !== 4) {
            return;
        }
        console.log(xhr.responseText + " percent downloaded!");
        if (xhr.responseText === "100") {
            return; /*stop checking for progress when its finished*/
        }
        setTimeout(checkProgress, 1000); //<<check for progress every 1 second

    });
    xhr.send();
})();

important edit: as @drew010 pointed out, it won't work without session_write_close();session_start(); each time curl updates the value, fixed that.

Quote from cURL download progress in PHP

echo "<pre>";
echo "Loading ...";

ob_flush();
flush();

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://stackoverflow.");
//curl_setopt($ch, CURLOPT_BUFFERSIZE,128);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, 'progress');
curl_setopt($ch, CURLOPT_NOPROGRESS, false); // needed to make progress function work
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
$html = curl_exec($ch);
curl_close($ch);


function progress($resource,$download_size, $downloaded, $upload_size, $uploaded)
{
    if($download_size > 0)
         echo $downloaded / $download_size  * 100;
    ob_flush();
    flush();
    sleep(1); // just to see effect
}

echo "Done";
ob_flush();
flush();

?>

Don't use ob_flush(), just flush(). (Remove all the ob_ functions). Also, consider modifying the buffersize:

curl_setopt($ch, CURLOPT_BUFFERSIZE, 16000);

Experiment with the size.

PHP will show the results when all the scripts are done. This is the reason that you just the finish progress.

You can execute the curl downloading in the background, and write the progress to session, database, session or memory such as redis, memchche. Then in the client side read the process every very short time.

You also can use Iframe to do this.

本文标签: javascriptPHP cURL download progress using jqueryStack Overflow