微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

从带有 PHP 的 IIS Express 服务器使用 Powershell 下载文件随机停止

如何解决从带有 PHP 的 IIS Express 服务器使用 Powershell 下载文件随机停止

情况:

我们有一个小应用程序,您可以在其中选择诸如“我想要版本 X.X.X,Nightly,Branch:XYZ”之类的内容,该应用程序将根据您的规范从构建服务器获取最新的包。

问题:

在 100 种情况中的 95 种情况下,包的下载都有效,但在所有情况下,有 5% 的情况下下载只是在下载随机数量的数据后停止。当你重试同样的事情时,它可能会起作用,或者有时它可能会以不同的下载量停止。您可能已经猜到了,这不是一个非常可接受的成功率。

在读取随机数量的数据后,powershell 脚本随机停止在 $response_stream.Read($buffer,$buffer.length)(参见下面的脚本)。下载速度不会越来越小然后停止。它只是完全停止,好像服务器不再发送任何东西一样,几分钟后我们收到超时错误(“使用 3 个参数调用“读取”时出现异常:已超过进程超时”)。

我尝试了很多东西,但我没有想法(基本上是尝试在 IIS 上增加超时,尝试使用 powershell 下载的不同方法搜索问题,但都没有成功)。这也会在随机时间发生,在晚上(很少用户),也可以在白天(很多用户)发生。 有没有人知道如何或如何尝试使下载更加健壮?我的猜测是 PHP Script/IIS 有问题,但遗憾的是我对两者的了解都非常有限......

附加信息:

客户端的下载是通过 Powershell 完成的。我写了一个整洁的小函数来处理这个:

function Get-FileFromURL
{
    param(
        [Parameter(Mandatory,Position = 0)] [System.Uri]$URL,[Parameter(Mandatory,Position = 1)] [string]$OutputFile,Position = 2)] [System.Management.Automation.PsCredential]$Credentials,[Parameter(Position = 3)]            [string]$Filename
    )

    try
    {
        $request = [System.Net.HttpWebRequest]::Create($URL)
        $request.Credentials = $Credentials;
        $request.set_Timeout(5000) # 5 second timeout
        $response = $request.GetResponse()
        $total_bytes = $response.ContentLength
        $response_stream = $response.GetResponseStream()
        $header = $response.Headers.Get("content-disposition");
        
        if($Filename -eq "")
        {
            $Filename = ($header -split "=")[1].Trim("`"");
            $OutputFile = $OutputFile + "\" + $Filename; 
        }

        try 
        {
            # 256KB works better on my machine for 1GB and 10GB files
            # See https://www.microsoft.com/en-us/research/wp-content/uploads/2004/12/tr-2004-136.pdf
            # Cf. https://stackoverflow.com/a/3034155/10504393
            $buffer = New-Object -TypeName byte[] -ArgumentList 256KB
            $target_stream = [System.IO.File]::Create($OutputFile)
            $stopwatch =  [system.diagnostics.stopwatch]::StartNew();
            $refreshTimerInMilliseconds = 1000;
            $refreshTimer = $refreshTimerInMilliseconds / 1000;

            do
            {
                $count = $response_stream.Read($buffer,$buffer.length)
                $target_stream.Write($buffer,$count)
                $downloaded_bytes = $downloaded_bytes + $count

                if ($stopwatch.ElapsedMilliseconds -ge $refreshTimerInMilliseconds)
                {
                    $percent = $downloaded_bytes / $total_bytes;
                    $speed = ($downloaded_bytes - $prev_downloaded_bytes) / 1KB / $refreshTimer;
                    $status = @{
                        completed  = "{0,6:p2} Completed" -f $percent
                        downloaded = "{0:n0} MB of {1:n0} MB" -f ($downloaded_bytes / 1MB),($total_bytes / 1MB)
                        speed      = "{0,7:n0} KB/s" -f ($speed)
                        eta        = "Time Remaining: {0:hh\:mm\:ss}" -f (New-TimeSpan -Seconds (($total_bytes - $downloaded_bytes) / 1KB / $speed))
                    }
                    $progress_args = @{
                        Activity        = "Downloading '$Filename'"
                        Status          = "$($status.completed) ($($status.downloaded)),$($status.speed),$($status.eta)"
                        PercentComplete = $percent * 100
                    }
                    
                    Write-Progress @progress_args

                    $prev_downloaded_bytes = $downloaded_bytes
                    $stopwatch.Restart();
                }
            } while ($count -gt 0)

            $stopwatch.Stop();
        }
        finally 
        {
            if ($target_stream) { $target_stream.dispose() }
            # If file exists and $count is not zero or $null,then script was interrupted by user
            if ((Test-Path $Filename) -and $count) { Remove-Item -Path $Filename }
        }
    }
    finally 
    {
        if ($response)        { $response.dispose() }
        if ($response_stream) { $response_stream.dispose() }
    }
}

在服务器端,我们有一个带有 PHP 脚本的 IIS Express,用于提供文件

if (is_file($file_path))
{
    $file_size  = filesize($file_path);
    $file = @fopen($file_path,"rb");
    
    if ($file)
    {
        // set the headers,prevent caching
        header("Pragma: public");
        header("Expires: -1");
        header("Cache-Control: public,must-revalidate,post-check=0,pre-check=0");
        header("Content-disposition: attachment; filename=\"$file_name\"");
        
        // set appropriate headers for attachment or streamed file
        $is_attachment = isset($_REQUEST['stream']) ? false : true;
        if ($is_attachment)
            header("Content-disposition: attachment; filename=\"$file_name\"");
        else
            header('Content-disposition: inline;');
        
        // set the mime type based on extension,add yours if needed.
        $ctype_default = "application/octet-stream";
        $content_types = array(
            "exe" => "application/octet-stream","zip" => "application/zip","mp3" => "audio/mpeg","mpg" => "video/mpeg","avi" => "video/x-msvideo",);
        $ctype = isset($content_types[$file_ext]) ? $content_types[$file_ext] : $ctype_default;
        header("Content-Type: " . $ctype);
        
        //check if http_range is sent by browser (or download manager)
        if(isset($_SERVER['HTTP_RANGE']))
        {
            list($size_unit,$range_orig) = explode('=',$_SERVER['HTTP_RANGE'],2);
            if ($size_unit == 'bytes')
            {
                //multiple ranges Could be specified at the same time,but for simplicity only serve the first range
                //http://tools.ietf.org/id/draft-ietf-http-range-retrieval-00.txt
                list($range,$extra_ranges) = explode(',',$range_orig,2);
            }
            else
            {
                $range = '';
                header('HTTP/1.1 416 Requested Range Not Satisfiable');
                exit;
            }
        }
        else
        {
            $range = '';
        }
        
        //figure out download piece from range (if set)
        list($seek_start,$seek_end) = explode('-',$range,2);
        
        //set start and end based on range (if set),else set defaults
        //also check for invalid ranges.
        $seek_end   = (empty($seek_end)) ? ($file_size - 1) : min(abs(intval($seek_end)),($file_size - 1));
        $seek_start = (empty($seek_start) || $seek_end < abs(intval($seek_start))) ? 0 : max(abs(intval($seek_start)),0);
        
        //Only send partial content header if downloading a piece of the file (IE workaround)
        if ($seek_start > 0 || $seek_end < ($file_size - 1))
        {
            header('HTTP/1.1 206 Partial Content');
            header('Content-Range: bytes '.$seek_start.'-'.$seek_end.'/'.$file_size);
            header('Content-Length: '.($seek_end - $seek_start + 1));
        }
        else
            header("Content-Length: $file_size");
        
        header('Accept-Ranges: bytes');
        
        set_time_limit(0);
        fseek($file,$seek_start);
        
        while(!feof($file))
        {
            print(@fread($file,1024*8));
            ob_flush();
            flush();
            if (connection_status()!=0)
            {
                @fclose($file);
                exit;
            }
        }
    
        // file save was a success
        @fclose($file);
        exit;
    }
    else
    {
        // file Couldn't be opened
        header("HTTP/1.0 500 Internal Server Error");
        exit;
    }
}
else
{
    // file does not exist
    header("HTTP/1.0 404 File not found");
    exit;
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。