访问远程资源

 第一步: 准备需要的一些配置

//抓取资源地址
$strUrl = 'http://test.com/77.mp4';

//保存地址
$strName = '111.mp4';

$arrHeader = [
    'http'=>[
        'method'=>"GET",
        'header'=>"Accept-language: en\r\n" .
            //假设检测了Cookie令牌, 就拼接令牌给他检测
            "Cookie: foo=bar\r\n".
            //假设服务器检测来来源必须是指定网站,继续拼
            "Referer: https://www.baidu.com\r\n"
    ]
];

$streamHeader = stream_context_create($arrHeader);

$resFp = fopen($strUrl, 'r', false, $streamHeader);

 第二步: 开始抓取

方案一(传统):逐行读取写入文件

//读取报文头信息,读取一些基本信息,也可做一些检测
$arrMeta = stream_get_meta_data($resFp);

//遍历一下HTML header 头信息, 读取一下文件大小
foreach ($arrMeta['wrapper_data'] as $v)
{
    if(is_numeric(strpos($v, 'Content-Length')))
    {
        $intTotal = trim(strrchr($v, ':')," :");
    }

}

//开始读取资源,写入到指定位置
while (!feof($resFp))
{
    $buffer = fread($resFp,8192);

    file_put_contents($strName,$buffer,FILE_APPEND);

    $intTotal -= strlen($buffer);

    if($intTotal<=0)break;
}

方案二:既然我们已经是资源的的形式打开,那我们可是使用文件管道直接传输,这个方案内存使用比较少,如果想要更快速的执行,可以考虑从资源header头里获取资源大小,然后填充至该函数第三个参数

$resSaveFp = fopen($strName, "w");
stream_copy_to_stream($resFp, $resSaveFp);
fclose($resSaveFp);

方案三:如果资源希望直接抛出至终端,而不是保存下来,则应该使用readfile()避免文件过大,内存问题

readfile($strUrl, false, $streamHeader);

方案四:当然,如果资源希望直接抛出至终端, 使用fread也可以,只不过需要将内存数据及时的抛出

while (!feof($resFp))
{
    $buffer = fread($resFp,8192);
    // flush output
	echo buffer;
	ob_flush();
	flush();
    $intTotal -= strlen($buffer);
    if($intTotal<=0)break;
}

方案五:当然,如果资源希望直接抛出至终端, 使用php://stdout 基础协议也可以的

$resSaveFp = fopen("php://output","w");
stream_copy_to_stream($resFp, $resSaveFp);
fclose($resSaveFp);

最后: 关闭资源

fclose($resFp);
exit;

总结

  • 熟悉对方服务器对资源获取的条件和如何传参,可使用POSTMAN工具慢慢调试检测
  • 如果是资源比较大,就应全部读取至内存,导致内存溢出,特别不能使用 file_get_content这样的函数
  • 如果只是伪装成一个类似代理的结构,而不保存资源,直接抛出, 则应该使用readfile避免掉大内存使用,也可fread逐行读取,但是需要赶紧flush出去