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

Laravel PHP上传录制视频至AWS S3速度慢且易失败问题咨询

视频上传优化方案(Laravel + AWS S3)

问题根源分析

当前代码存在几个核心问题导致上传慢、易失败:

  • 转码和上传同步执行,单个请求耗时过长,容易触发超时
  • 用file_get_contents把整个视频加载到内存,大文件会耗尽服务器内存导致上传失败
  • 未利用S3的分块上传和传输加速能力

一、AWS Transfer Acceleration(传输加速)

需要AWS端配置 + 代码配合:

  1. AWS控制台配置:进入目标S3桶的「属性」页面,找到「传输加速」选项,开启该功能(按指引完成域名所有权验证即可)
  2. Laravel代码配置:修改config/filesystems.php中的S3驱动配置,添加加速端点:
's3' => [
    // 保留原有key/secret/region/bucket等配置
    'endpoint' => 'https://你的桶名.s3-accelerate.amazonaws.com',
    'use_path_style_endpoint' => false,
],

开启后,S3会通过CloudFront全球边缘节点加速上传,减少跨区域传输延迟。


二、Multipart Upload(分块上传)

Laravel原生支持自动分块,只需调整代码写法:
当前代码用file_get_contents('output.mp4')读取整个文件,改成用文件流或直接传入文件路径,Laravel的S3驱动会自动对10MB以上的文件进行分块上传(可通过multipart_threshold参数调整阈值):

优化上传代码片段

// 替换原来的上传代码
$file_folder = 'uploads/video/';
// 用putFileAs直接传入文件路径,自动处理分块
$store = Storage::disk('s3')->putFileAs(
    $file_folder,
    public_path('output.mp4'),
    $file_name
);

这种方式不会把整个文件加载到内存,降低服务器内存占用,同时分块上传失败后可以重试单个块,提升成功率。


三、核心流程优化(解决超时问题)

把转码+上传放到Laravel队列异步处理,避免单个请求长时间阻塞:

1. 创建队列任务

执行命令生成任务类:

php artisan make:job ProcessVideoUpload

2. 编写任务逻辑

<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Storage;
use FFMpeg\FFMpeg;
use FFMpeg\Format\Video\X264;

class ProcessVideoUpload implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $tempFilePath;
    protected $oldFile;
    protected $fileName;

    public function __construct($tempFilePath, $oldFile = null)
    {
        $this->tempFilePath = $tempFilePath;
        $this->oldFile = $oldFile;
        $this->fileName = str_replace('/', '', \Hash::make(time())) . '.mp4';
    }

    public function handle()
    {
        try {
            // FFmpeg转码(可选:添加硬件加速参数加快转码)
            $ffmpeg = FFMpeg::create();
            $video = $ffmpeg->open($this->tempFilePath);
            
            $format = new X264();
            // 服务器有NVIDIA显卡的话,启用硬件加速大幅提升转码速度
            // $format->setAdditionalParameters(['-c:v', 'h264_nvenc']);
            
            $outputPath = storage_path('app/temp/' . $this->fileName);
            $video->save($format, $outputPath);

            // 分块上传到S3
            $fileFolder = 'uploads/video/';
            Storage::disk('s3')->putFileAs($fileFolder, $outputPath, $this->fileName);

            // 清理临时文件
            unlink($outputPath);
            unlink($this->tempFilePath);

            // 删除旧文件(如果存在)
            if ($this->oldFile && Storage::disk('s3')->exists($fileFolder . basename($this->oldFile))) {
                Storage::disk('s3')->delete($fileFolder . basename($this->oldFile));
            }

            // 这里可以添加通知逻辑(比如用websocket告知前端上传完成)
        } catch (\Exception $e) {
            \Log::error('视频处理失败: ' . $e->getMessage() . ' 行号: ' . $e->getLine());
            // 清理临时文件
            if (file_exists($this->tempFilePath)) unlink($this->tempFilePath);
            if (isset($outputPath) && file_exists($outputPath)) unlink($outputPath);
            throw $e; // 让队列自动重试或标记失败
        }
    }
}

3. 修改控制器方法

public function video_upload(Request $request)
{
    $response = [
        'status' => false,
        'data' => []
    ];
    $responseCode = 200;

    $validator = Validator::make($request->all(), [
        'file' => 'required|file|mimes:mp4,webm,ogg',
    ]);

    if ($validator->fails()) {
        $response['data']['validator'] = $validator->errors();
        return response()->json($response, $responseCode);
    }

    try {
        // 先把前端上传的文件存到本地临时目录
        $tempFile = $request->file('file');
        $tempFilePath = $tempFile->store('temp', 'local');
        $fullTempPath = storage_path('app/' . $tempFilePath);

        // 分发队列任务,异步处理转码和上传
        ProcessVideoUpload::dispatch($fullTempPath, $request->old_file);

        $response['status'] = true;
        $response['data'] = '视频正在后台处理,完成后会通知你'; // 或返回任务ID供前端查询状态

    } catch (\Exception $e) {
        $response['data']['message'] = $e->getMessage() . ' 行号: ' . $e->getLine();
        $responseCode = 400;
    }

    return response()->json($response, $responseCode);
}

四、额外优化建议

  • FFmpeg转码优化:调整CRF值(比如设为28,平衡画质和速度)、启用硬件加速(如NVENC/QSV),大幅减少转码时间
  • 前端预处理:录制时直接生成MP4格式,或前端用JS压缩视频后再上传,减少后端压力
  • 日志完善:添加详细的转码、上传日志,方便排查失败原因

内容的提问来源于stack exchange,提问作者Sara

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 14:06:56