简体   繁体   English

PHP:同一客户端同时运行多个脚本

[英]PHP: Running Multiple Scripts at the Same Time for the Same Client

I have one PHP script that can take several minutes to complete. 我有一个PHP脚本,可能需要几分钟才能完成。 The script downloads a file to the user PC. 该脚本将文件下载到用户PC。

I have another PHP script and its role is to monitor progress of the main download script. 我还有另一个PHP脚本,它的作用是监视主要下载脚本的进度。 That script is called by the client via AJAX calls and should return download progress information. 客户端通过AJAX调用来调用该脚本,并应返回下载进度信息。

Right now, my tests show, that during the execution of the main script(in other words, during file download), the AJAX - monitor script returns no values at all. 现在,我的测试表明,在执行主脚本期间(换句话说,在文件下载期间),AJAX-监视脚本根本不返回任何值。 It starts behaving normally, when the main - Download script finishes. 当主-下载脚本完成后,它开始正常运行。

Is it possible that PHP can not run two or more scripts simultaneously and it allows to run script only in sequential order? PHP是否可能无法同时运行两个或多个脚本,而只能按顺序运行脚本?

I could insert my code, but I think for the purpose of my question, it is not needed. 我可以插入我的代码,但是我认为出于我的问题,这不是必需的。 I simply need to know, if two or more PHP scripts may run simultaneously for the same client. 我只需要知道同一客户端是否可以同时运行两个或多个PHP脚本。

I use: 我用:

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

Code Used: 使用的代码:

As I was asked to show you my code, please, see the below code parts. 当要求我向您显示我的代码时,请参阅以下代码部分。

Main PHP(later Download) Script: 主要的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();
}
?>

Monitoring PHP script: 监视PHP脚本:

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

JavaScript code, where I call the both PHP scripts: 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: 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();
    }
};

Code Update I: 代码更新一:

PHP Script that will later download files: PHP脚本,以后将下载文件:

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

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

PHP Monitoring script that outputs test values: PHP监视脚本输出测试值:

<?php
$test_value = 25;

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

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

Both scripts are called followingly: 以下两个脚本均被调用:

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

Without more info there are a few possibilities here, but I suspect that the issue is your session. 没有更多信息,这里有几种可能性,但我怀疑问题出在您的会议上。 When a script that uses the session file start, PHP will lock the session file until session_write_close() is called or the script completes. 当使用会话文件的脚本启动时,PHP将锁定会话文件,直到调用session_write_close()或脚本完成为止。 While the session is locked any other files that access the session will be unable to do anything until the first script is done and writes/closes the session file (so the ajax calls have to wait until the session file is released). 当会话被锁定时,访问该session所有其他文件将无法执行任何操作,直到完成第一个脚本并写入/关闭该会话文件为止(因此ajax调用必须等待该会话文件被释放)。 Try writing the session as soon as you've done validation, etc on the first script and subsequent scripts should be able to start. 完成验证后,尝试在第一个脚本上立即编写会话,并且随后的脚本应该能够启动。

Here's a quick and dirty approach: 这是一种快速而肮脏的方法:

The "Landing" page: “着陆”页面:

This is the page that the user is going to click the download link 这是用户要单击下载链接的页面

<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>

The "File Uploader" “文件上传器”

This is the PHP script that serves the large file... it breaks it into chunks and after sending each chunk it closes the session so the session becomes available to other scripts. 这是服务于大文件的PHP脚本...将其分成多个块,并在发送每个块后关闭会话,以便其他脚本可以使用该会话。 Also notice that I've added a ignore_user_abort / connection_aborted handler so that it can take a special action should the connection be terminated. 还要注意,我添加了ignore_user_abort / connection_aborted处理程序,以便在连接终止时可以采取特殊措施。 This is the section that actually deals with the session_write_close() issue , so focus on this script. 这是实际处理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');
?>

The "File Monitor" “文件监视器”

This is a script that is called via Ajax and is in charge of reporting progress back to the Landing Page. 这是一个通过Ajax调用的脚本,负责将进度报告回登陆页面。

<?
session_start();
echo $_SESSION['file_progress'];
?>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM