PHP + NGINX 控制视频文件播放,并防止文件下载
最简单的方法是使用NGINX的 internal 功能
server {
listen 80;
server_name www.xxx.com;
location / {
index index.php index.html index.htm;
root /xxx;
if (!-e $request_filename) {
rewrite ^/index.php(.*)$ /index.php?s=$1 last;
rewrite ^(.*)$ /index.php?s=$1 last;
break;
}
}
# 这里使用internal做下载防护,只允许内部程序(PHP等)访问,这样外部直接访问这个地址就会提示404错误
location ~ \.mp4$ {
internal;
# 这里的路径配置是可选的,可以配置到网站外部,和其他location里的配置路径是一个意思,可以更好的防止文件被通过网址下载
root /bbb;
}
location ~ \.php$ {
root /xxx;
try_files $uri =404;
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
然后在PHP中通过header('Location: /xxx/aa.mp4')访问,但这个只适合于小文件,因为这种方式不支持Content-Range和HTTP 206,导致不支持断点续传和视频文件的边下边播
最好的方式是通过header('X-Accel-Redirect: /xxx/aa.mp4')访问,X-Accel-Redirect支持Content-Range和HTTP 206,Apache里面是X-Sendfile
如果不能配置nginx的internal选项,或者nginx不支持X-Accel-Redirect,却要使用断点续传、边下边播和下载防护,那么就要通过PHP代码来控制
思路上是使用token鉴权,首先下载文件前由前端请求一个token,这个token由用户ID+时间戳+视频ID组成,然后存储到session并加密后发送给前端
$token = $_SESSION['uid'] . '|' . time() . '|' . $id;
// 保存未加密token到seesion
$_SESSION['video_token'] = $token;
// 加密token以便发送到客户端
$token = Mcrypt::encode($token, 'lbnnbs');
前端收到token,向下载控制器网址发送token请求文件下载,控制器对token解码,判断Session是否一致、用户ID是否正确、是否超时、视频ID是否正确
然后将视频ID转换为文件地址,通过PHP读取文件发送给前端下载。并通过发送Content-Range和HTTP 206来支持断点续传、边下边播等操作
$id = decodeIdFromToken($token); $file = getFilePath($id); SendVideo($file);

private function SendVideo($file) {
header("Content-type: video/mp4");
header("Accept-Ranges: bytes");
$size = filesize($file);
if (isset($_SERVER['HTTP_RANGE'])) {
header("HTTP/1.1 206 Partial Content");
list($name, $range) = explode("=", $_SERVER['HTTP_RANGE']);
list($begin, $end) = explode("-", $range);
if ($end == 0)
$end = $size - 1;
}else {
$begin = 0;
$end = $size - 1;
}
header("Content-Length: " . ($end - $begin + 1));
header("Content-Disposition: filename=" . basename($file));
header("Content-Range: bytes " . $begin . "-" . $end . "/" . $size);
$fp = fopen($file, 'rb');
fseek($fp, $begin);
while (!feof($fp)) {
$p = min(1024, $end - $begin + 1);
$begin += $p;
echo fread($fp, $p);
}
fclose($fp);
}
private function decodeIdFromToken($token) {
if (empty($_SESSION['uid'])) {
return false;
}
$token = Mcrypt::decode($token, 'lbnnbs');
if ($token != $_SESSION['video_token']) {
//token解密失败,判定失效
return false;
}
$token_arr = explode('|', $token);
if (intval($token_arr[0]) != $_SESSION['uid']) {
// token不是当前用户,判定失效
return false;
}
if (intval($token_arr[1]) < time() - 10) {
// token生成于10秒前,判定失效
return false;
}
unset($_SESSION['video_token']);
return $token_arr[2]; // 返回id
}

另外需要注意的是,很多时候mp4文件会无法支持边下边播,这是因为在转码或者压缩视频文件到mp4的时候把文件的元数据移除了或者放到了文件的末尾,导致前端播放器不能第一时间获取到视频文件的播放时长等信息,只能全部下载完毕后或者直到读取到了元数据后才能播放。这个时候可以使用qt-faststart.exe工具进行处理,把元数据放回文件头部即可。
PHP + NGINX 控制视频文件播放,并防止文件下载的更多相关文章
- 网站上flv,MP4等格式的视频文件播放不出来的解决办法
在做一个网站时,发现视频文件,比如flv,MP4格式在本地可以正常的播放,但是传到了开发机器上,就不行了.播放器的文件地址是对的,就是一直没有反应. 经过长时间的实验,发现问题在与iis的设置问题.i ...
- Emgu 学习(2) 视频文件播放
播放AVI视频文件 static void Main(string[] args) { CvInvoke.NamedWindow("TestVideo", NamedWindowT ...
- JS控制视频的播放
在用js定时器控制视频时, html部分: <video id="video_1"> <source id="source_1" src=&q ...
- C#获取视频文件播放长度
下面两种方法只支持部分视频格式,一般格式mp3,wma等等支持 1.使用Shell32 添加引用,选择COM中的Microsoft Shell Controls And Automation引用 // ...
- Video组件:控制视频的播放与暂停
来自<sencha touch权威指南>第10章,315页开始 app.js代码如下: Ext.require(['Ext.Video','Ext.MessageBox','Ext.Too ...
- H5网页播放器播不了服务器上的mp4视频文件
打开IIS,在功能视图里找到MIME类型菜单,打开该菜单后鼠标右键添加.mp4扩展名的MIME类型video/mp4 其他视频文件播放不了估计也得在IIS里添加对应的MIME类型(从服务器下载文件时也 ...
- C# WPF 用MediaElement控件实现视频循环播放
在WPF里用MediaElement控件,实现一个循环播放单一视频的程序,同时可以控制视频的播放.暂停.停止. 一种方式,使用MediaElement.MediaEnded事件,在视频播放结束后,自动 ...
- Unity中带有alpha通道的视频叠加播放
问题: 如何让两个透明视频叠加播放 解决播放: 1:使用Unity自带的shader,shader代码如下所示 Shader "Unlit/MaskVideo" { Propert ...
- 深入理解MVC C#+HtmlAgilityPack+Dapper走一波爬虫 StackExchange.Redis 二次封装 C# WPF 用MediaElement控件实现视频循环播放 net 异步与同步
深入理解MVC MVC无人不知,可很多程序员对MVC的概念的理解似乎有误,换言之他们一直在错用MVC,尽管即使如此软件也能被写出来,然而软件内部代码的组织方式却是不科学的,这会影响到软件的可维护性 ...
随机推荐
- 单元素枚举类型singleton模块
public enum Elvis { INSTANCE; public void leaveTheBuilding() { System.out.println("Whoa baby, I ...
- [转]SuperSocket
public class SocketServer : AppServer<AppSession> { public SocketServer() : base(new DefaultRe ...
- jQuery绑定和解绑点击事件及重复绑定解决办法
原文地址:http://www.111cn.net/wy/jquery/47597.htm 绑点击事件这个是jquery一个常用的功能,如click,unbind等等这些事件绑定事情,但还有很多朋友不 ...
- Flume原理解析【转】
一.Flume简介 flume 作为 cloudera 开发的实时日志收集系统,受到了业界的认可与广泛应用.Flume 初始的发行版本目前被统称为 Flume OG(original generati ...
- 05-RARP: 逆地址解析协议
具有本地磁盘的系统引导时,一般是从磁盘上的配置文件中读取I P地址.但是无盘机,如X终端或无盘工作站,则需要采用其他方法来获得I P地址. 网络上的每个系统都具有唯一的硬件地址,它是由网络接口生产厂家 ...
- SCCM2012 R2实战系列之十二:解决OSD分发时间过长的问题
对于SCCM 2012 R2的初学者来说,能够成功分发操作系统可能已经是非常兴奋了.但在企业中会遇到客户提出的各种各样苛刻的需求.所以在平时实验过程中多站点客户的角度想问题,尽可能的贴近企业实际生产环 ...
- c#day05
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ccc ...
- phpstudy远程连接mysql
格局如图所示执行以下命令 mysql -u root -p mysql>use mysql; mysql>select 'host' from user where user='root' ...
- AS导入项目报错:Plugin with id 'com.android.application' not found.
从github或第三方Demo中获取的项目导入到AndroidStudio中报错Plugin with id 'com.android.application' not found.:今天导入一个讯飞 ...
- solr学习(六):使用自定义int/long类型主键
需求分析: 我不想使用solr默认的主键id,我想换成其他的,比如我的文章id为article_id,我想让article_id作为主键. 而且,我的主键是int类型,而solr的主键默认是strin ...