You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:27:52