第一步: 准备需要的一些配置
//抓取资源地址
$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出去