【问题标题】:PHP cURL download progress using jquery使用jquery的PHP cURL下载进度
【发布时间】:2015-03-30 05:36:44
【问题描述】:

我不知道如何在每次使用 php 更新时将进度推送出去。为了清楚起见,我将写一个例子。

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();

我遇到的问题是它在完成后返回进度,下载时没有推送它。如果您有任何建议,请提前感谢。

【问题讨论】:

  • 您好,您有这个问题的解决方案吗....
  • 一些想法: 1. 负载均衡器可能会无意中保存渐进式响应。 2. 您需要立即发送一定的字节数以使某些浏览器正常运行。 3. 您需要返回 HTML,并将数据有条不紊地包装在 <script> 标记中,以使用像这样的简单 COMET 方法。 4. 我会考虑重新实现使用临时文件来存储百分比,并 ping 另一个简单的脚本以从文件中获取该值,绕过上面的脚本。 5. “SSE”,又名EventSource 是跨浏览器,易于在 PHP 中实现,以替换 #4 中的 ping...

标签: javascript php jquery curl


【解决方案1】:

使用这种方法,你会遇到各种缓存问题,是 php 输出缓冲吗?你会有问题。你在网络服务器后面,ala nginx/apache/lighthttp/anything?你会有问题。是浏览器缓存输出吗? (所有主流浏览器都有),你会遇到问题。

我建议一个没有这些问题的替代方案:使用 $_SESSION 存储下载百分比,并使用 XMLHttpRequests 查询百分比(实际上通过 WebSockets 查询百分比是最佳的、无滞后的、使用更少的 bw 等,但很多更难实现)

下载movie.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']??'?';

然后在浏览器中监控进度:

(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();
})();

重要编辑:正如@drew010 指出的那样,没有 session_write_close();session_start(); 它将无法工作每次 curl 更新值时,都会修复它。

【讨论】:

  • 我认为这是最好的解决方案,但不要忘记每次更新后你需要使用session_write_close,然后再使用session_start,否则Ajax请求会一直卡住,直到会话关闭,因为它将被锁定并阻止使用同一会话的后续请求。
  • 为什么要将 session_start 包装在一个 if 中,如果它首先调用它,它会从 cookie 中检查 session_id,如果它知道加载它。
  • @drew010 哇。我不知道,谢谢!我会在一秒钟内更新代码
  • @MartinBarker 我不知道在调用实际的 php 文件之前运行什么,但是在这种情况下,某些站点在 auto_prepend_file php.ini 指令或类似 xx 中开始会话,调用 session_begin( ) 如果不检查它是否已经开始,会导致错误(我相信是 E_NOTICE?) - 刚刚检查过,是的:Notice: A session had already been started - ignoring session_start() in C:\WTServer\WWW\sesstest\2.php on line 4
  • 奇怪的是,如果我尝试在没有会话开始的情况下使用 $_SESSION,即使会话已经在其中对我不起作用......
【解决方案2】:

引用cURL download progress in PHP

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

ob_flush();
flush();

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://stackoverflow.com");
//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();

?>

【讨论】:

    【解决方案3】:

    PHP 将在所有脚本完成后显示结果。这就是你刚刚完成进度的原因。

    可以在后台执行curl下载,将进度写入session、数据库、session或redis、memchche等内存。然后在客户端每隔很短的时间读取一次进程。

    您也可以使用 iframe 来执行此操作。

    【讨论】:

      【解决方案4】:

      不要使用 ob_flush(),只使用 flush()。 (删除所有 ob_ 函数)。 另外,考虑修改缓冲区大小:

      curl_setopt($ch, CURLOPT_BUFFERSIZE, 16000);
      

      用尺寸做实验。

      【讨论】:

        猜你喜欢
        • 2012-12-07
        • 2010-12-28
        • 1970-01-01
        • 1970-01-01
        • 2014-05-22
        • 2014-04-30
        • 1970-01-01
        • 1970-01-01
        • 2018-04-03
        相关资源
        最近更新 更多