如何解决从带有 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 举报,一经查实,本站将立刻删除。