0

我有一个 PHP 脚本可能需要几分钟才能完成。该脚本将文件下载到用户 PC。

我有另一个 PHP 脚本,它的作用是监控主下载脚本的进度。该脚本由客户端通过 AJAX 调用调用,并应返回下载进度信息。

现在,我的测试表明,在执行主脚本期间(换句话说,在文件下载期间),AJAX - 监控脚本根本不返回任何值。当主 - 下载脚本完成时,它开始正常运行。

PHP是否有可能不能同时运行两个或多个脚本,它只允许按顺序运行脚本?

我可以插入我的代码,但我认为出于我的问题的目的,不需要它。我只需要知道,两个或多个 PHP 脚本是否可以同时为同一个客户端运行。

我用:

  • WAMP
  • PHP 版本 5.4.12
  • 没有 jQuery 的 JavaScript

使用的代码:

当我被要求向您展示我的代码时,请参阅下面的代码部分。

主要 PHP(稍后下载)脚本:

<?php    
    // disable script expiry
    set_time_limit(0);

// start session if session is not already started
if (session_status() !== PHP_SESSION_ACTIVE)
{
    session_start();
}    

// prepare session variable
$_SESSION['DownloadProgress'] = 0;    

for( $count = 0; $count < 60; $count++)
{
    sleep(1);

    echo "Iteration No: " . $count;

    $_SESSION['DownloadProgress']++;
    echo '$_SESSION[\'DownloadProgress\'] = ' . $_SESSION['DownloadProgress'];
    flush();
    ob_flush();
}
?>

监控 PHP 脚本:

// construct JSON
$array = array("result" => 1, "download_progress" => $_SESSION['DownloadProgress']);
echo json_encode($array);
?>

JavaScript 代码,我将这两个 PHP 脚本称为:

   SearchResults.myDownloadFunction = function()
    {
        console.log( "Calling: PHP/fileDownload.php" );
        window.location.href = 'PHP/fileDownload.php?upload_id=1';


        console.log( "Calling: getUploadStatus()" );
        FileResort.SearchResults.getUploadStatus();

        console.log( "Called both functions" );   
    };

JavaScript AJAX:

// call AJAX function to get upload status from the server
SearchResults.getUploadStatus = function ()
{

    var SearchResultsXMLHttpRequest = FileResort.Utils.createRequest();

    if (SearchResultsXMLHttpRequest == null)
    {
        console.log("unable to create request object.");
    }
    else
    {
        SearchResultsXMLHttpRequest.onreadystatechange = function ()
        {
            console.log("Response Text: " + SearchResultsXMLHttpRequest.responseText);
            console.log("AJAX Call Returned");

            if ((SearchResultsXMLHttpRequest.readyState == 4) && (SearchResultsXMLHttpRequest.status == 200))
            {
                //if (that.responseJSON.result == "true")
                {
                    var responseJSON = eval('(' + SearchResultsXMLHttpRequest.responseText + ')');
                    console.log("Download Progress: " + responseJSON.download_progress);
                }
            }
        }

        var url = "PHP/fileDownloadStatus.php";
        SearchResultsXMLHttpRequest.open("POST", url, true);
        SearchResultsXMLHttpRequest.send();
    }
};

代码更新一:

稍后将下载文件的 PHP 脚本:

<?php
// disable script expiry
set_time_limit(0);

for( $count = 0; $count < 60; $count++)
{
    sleep(1);
}
?>

输出测试值的 PHP 监控脚本:

<?php
$test_value = 25;

// construct JSON
$array = array("result" => 1, "download_progress" => $test_value);

//session_write_close();
echo json_encode($array);
?>

两个脚本的调用如下:

SearchResults.myDownloadFunction = function()
{
    console.log( "Calling: PHP/fileDownload.php" );
    window.setTimeout(FileResort.SearchResults.fileDownload(), 3000);


    console.log( "Calling: getUploadStatus()" );
    window.setInterval(function(){FileResort.SearchResults.getDownloadStatus()}, 1000);

    console.log( "Called both functions" );    
};
4

1 回答 1

6

如果没有更多信息,这里有几种可能性,但我怀疑问题出在您的会话上。当使用会话文件的脚本启动时,PHP 将锁定会话文件,直到session_write_close()被调用或脚本完成。当会话被锁定时,任何其他访问的文件session将无法执行任何操作,直到第一个脚本完成并写入/关闭会话文件(因此 ajax 调用必须等到会话文件被释放)。尝试在第一个脚本上完成验证等后立即编写会话,后续脚本应该能够启动。

这是一种快速而肮脏的方法:

“登陆”页面:

这是用户要点击下载链接的页面

<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>
$(document).ready(function(e) {
    //Every 500ms check monitoring script to see what the progress is
    $('#large_file_link').click(function(){
        window.p_progress_checker  = setInterval( function(){
            $.get( "monitor.php", function( data ) {
                $( ".download_status" ).html( data +'% complete' );
                //we it's done or aborted we stop the interval
                if (parseInt(data) >= 100 || data=='ABORTED'){
                    clearInterval(window.p_progress_checker);
                }
                //if it's aborted we display that
                if (data=='ABORTED'){
                    $( ".download_status" ).html( data );
                    $( ".download_status" ).css('color','red').css('font-weight','bold');
                }
            })
        }, 500);
    });
});
</script>
</head>
<body>
    <div class="download_status"><!-- GETS POPULATED BY AJAX CALL --></div>
    <p><a href="download.php" id="large_file_link">Start downloading large file</a></p>
</body>
</html>

“文件上传器”

这是为大文件提供服务的 PHP 脚本......它将它分成块,并在发送每个块后关闭会话,以便会话可供其他脚本使用。另请注意,我添加了一个ignore_user_abort/connection_aborted处理程序,以便在连接终止时它可以采取特殊操作。这是实际处理该session_write_close()问题的部分,因此请关注此脚本。

<?php
/*Ignore user abort so we can catch it with connection_aborted*/
ignore_user_abort(true);

function send_file_to_user($filename) {

   //Set the appropriate headers:
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename='.basename($filename));
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: ' . filesize($filename));


   $chunksize = 10*(1024); // how many bytes per chunk (i.e. 10K per chunk)
   $buffer = '';
   $already_transferred =0;
   $file_size = filesize( $filename );
   $handle = fopen($filename, 'rb');
   if ($handle === false) {
       return false;
   }
   while (!feof($handle)) {
      /*if we're using a session variable to commnicate just open the session 
        when sending a chunk and then close the session again so that other 
        scripts which have request the session are able to access it*/ 
      session_start();

      //see if the user has aborted the connection, if so, set the status
      if (connection_aborted()) { 
            $_SESSION['file_progress'] = "ABORTED";
            return;
       }

       //otherwise send the next packet...
       $buffer = fread($handle, $chunksize);
       echo $buffer;
       ob_flush();
       flush();

       //now update the session variable with our progress
       $already_transferred += strlen($buffer);
       $percent_complete = round( ($already_transferred / $file_size) * 100);
       $_SESSION['file_progress'] = $percent_complete;

       /*now close the session again so any scripts which need the session
         can use it before the next chunk is sent*/
       session_write_close();
   }
   $status = fclose($handle);
   return $status;
} 

send_file_to_user( 'large_example_file.pdf');
?>

“文件监视器”

这是一个通过 Ajax 调用的脚本,负责将进度报告回登录页面。

<?
session_start();
echo $_SESSION['file_progress'];
?>
于 2013-10-29T19:23:30.960 回答