PHP返回MP4视频时更新用户最后活跃时间的问题
解决PHP返回视频时同步更新用户最后活跃时间的问题
我来帮你搞定这个问题——当PHP直接输出MP4视频流时,前端JS确实没法运行,因为浏览器此时专注于处理视频资源,不会加载执行页面脚本。咱们得换后端思路,利用视频请求的时机来完成活跃时间更新,下面给你两个实用的方案:
方案一:直接在视频请求脚本中处理(适合无框架的原生PHP项目)
这个思路很直接:用户请求视频时,PHP在输出视频内容之前(或合适的时机)执行更新活跃时间的代码。需要注意的是,浏览器播放视频时会发送Range分段请求(比如用户拖拽进度条),所以要加个时间间隔判断,避免频繁更新数据库。
<?php session_start(); // 假设用户已登录,通过session获取用户ID,根据你的鉴权方式调整 $userId = $_SESSION['user_id'] ?? null; // 更新用户最后活跃时间的函数 function updateUserLastActive(int $userId): void { // 替换成你的数据库操作,这里以PDO为例 try { $pdo = new PDO('mysql:host=localhost;dbname=your_database', 'db_user', 'db_pass'); $stmt = $pdo->prepare("UPDATE users SET last_active = NOW() WHERE id = ?"); $stmt->execute([$userId]); } catch (PDOException $e) { // 可以记录错误日志,避免影响视频播放 error_log("更新活跃时间失败: " . $e->getMessage()); } } $videoPath = 'path/to/video.mp4'; if (!file_exists($videoPath)) { http_response_code(404); exit("视频不存在"); } // 仅当距离上次更新超过5分钟时才执行更新,减少数据库压力 $lastUpdateTime = $_SESSION['last_active_update_time'] ?? 0; if ($userId && time() - $lastUpdateTime > 300) { updateUserLastActive($userId); $_SESSION['last_active_update_time'] = time(); } // 处理视频流响应,支持Range分段请求(视频拖拽功能) $fileSize = filesize($videoPath); header('Content-Type: video/mp4'); header('Accept-Ranges: bytes'); header('Content-Disposition: inline;'); if (isset($_SERVER['HTTP_RANGE'])) { // 处理分段请求 preg_match('/bytes=(\d+)-(\d+)?/', $_SERVER['HTTP_RANGE'], $matches); $start = intval($matches[1]); $end = isset($matches[2]) ? intval($matches[2]) : $fileSize - 1; $length = $end - $start + 1; header("HTTP/1.1 206 Partial Content"); header("Content-Range: bytes $start-$end/$fileSize"); header("Content-Length: $length"); $file = fopen($videoPath, 'rb'); fseek($file, $start); echo fread($file, $length); fclose($file); } else { // 完整请求 header("Content-Length: $fileSize"); readfile($videoPath); } ?>
关键注意点:
- 必须处理
Range请求,否则用户无法拖拽视频进度条,同时也能兼容浏览器的分段加载逻辑。 - 加入时间间隔判断(比如5分钟),避免每次分段请求都触发数据库更新,降低资源消耗。
- 确保用户鉴权逻辑正确,能获取到有效的用户ID,避免更新错误的用户数据。
方案二:利用框架中间件(适合Laravel/Symfony等框架项目)
如果你的项目用了PHP框架,可以把活跃时间更新逻辑放到中间件里,和视频响应逻辑解耦,代码更优雅。以Laravel为例:
1. 创建更新活跃时间的中间件
// app/Http/Middleware/UpdateLastActive.php <?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; class UpdateLastActive { public function handle(Request $request, Closure $next) { // 仅当用户已登录,且距离上次更新超过5分钟时执行 if (Auth::check() && now()->diffInMinutes(Auth::user()->last_active ?? now()) >= 5) { Auth::user()->update(['last_active' => now()]); } return $next($request); } }
2. 在路由中应用中间件
// routes/web.php 或 routes/api.php use App\Http\Controllers\VideoController; Route::get('/stream-video/{filename}', [VideoController::class, 'stream']) ->middleware(UpdateLastActive::class);
3. 控制器处理视频流
// app/Http/Controllers/VideoController.php <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Storage; class VideoController extends Controller { public function stream(string $filename) { $videoPath = storage_path("app/videos/{$filename}"); if (!file_exists($videoPath)) { abort(404, "视频不存在"); } // Laravel的response()->file会自动处理Range请求 return response()->file($videoPath, [ 'Content-Type' => 'video/mp4', 'Content-Disposition' => 'inline', ]); } }
这个方案的优势是代码模块化,更新逻辑可以复用在其他需要记录活跃时间的路由上。
为什么前端JS方案行不通?
当PHP直接返回视频流时,浏览器会把这个请求识别为媒体资源请求,不会像处理HTML页面那样解析执行JS代码,所以你之前的前端脚本根本没有运行的环境,自然无法更新活跃时间。后端处理才是正确的思路。
内容的提问来源于stack exchange,提问作者Krash0
相关产品推荐
相关产品推荐

