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

如何在Laravel应用中通过YouTube API v3上传视频并保存URL到数据库

实现Laravel应用上传视频到YouTube并保存URL到数据库

1. 安装Google API客户端依赖

确保Laravel项目安装Google官方API客户端包,执行命令:

composer require google/apiclient:^2.0

2. 配置Google客户端与YouTube服务

在config/services.php中添加YouTube凭证配置:

return [
    // 其他服务配置...
    'youtube' => [
        'client_id' => env('YOUTUBE_CLIENT_ID'),
        'client_secret' => env('YOUTUBE_CLIENT_SECRET'),
        'redirect_uri' => env('YOUTUBE_REDIRECT_URI'),
        'access_token' => env('YOUTUBE_ACCESS_TOKEN'),
        'refresh_token' => env('YOUTUBE_REFRESH_TOKEN'),
        'scopes' => ['https://www.googleapis.com/auth/youtube.upload'],
    ],
];

在.env文件中填充对应凭证值(需确保OAuth 2.0凭证已申请youtube.upload权限)。

创建YouTube服务类封装上传逻辑:

// app/Services/YouTubeService.php
namespace App\Services;

use Google\Client;
use Google\Service\YouTube;
use Google\Http\MediaFileUpload;

class YouTubeService
{
    protected $youtube;

    public function __construct()
    {
        $client = new Client();
        $client->setClientId(config('services.youtube.client_id'));
        $client->setClientSecret(config('services.youtube.client_secret'));
        $client->setRedirectUri(config('services.youtube.redirect_uri'));
        $client->setScopes(config('services.youtube.scopes'));
        $client->setAccessType('offline');

        // 加载并刷新过期令牌
        $client->setAccessToken(config('services.youtube.access_token'));
        if ($client->isAccessTokenExpired()) {
            $newToken = $client->fetchAccessTokenWithRefreshToken(config('services.youtube.refresh_token'));
            // 可将新的access_token更新到.env或数据库
            file_put_contents(base_path('.env'), str_replace(
                'YOUTUBE_ACCESS_TOKEN=' . env('YOUTUBE_ACCESS_TOKEN'),
                'YOUTUBE_ACCESS_TOKEN=' . $newToken['access_token'],
                file_get_contents(base_path('.env'))
            ));
            $client->setAccessToken($newToken);
        }

        $this->youtube = new YouTube($client);
    }

    public function uploadVideo(string $filePath, string $title, string $description, string $privacyStatus = 'public'): \Google\Service\YouTube\Video
    {
        // 构建视频元数据
        $video = new YouTube\Video();
        $snippet = new YouTube\VideoSnippet();
        $snippet->setTitle($title);
        $snippet->setDescription($description);
        $snippet->setCategoryId('22'); // 默认分类:娱乐,可按需修改
        $video->setSnippet($snippet);

        $status = new YouTube\VideoStatus();
        $status->setPrivacyStatus($privacyStatus);
        $video->setStatus($status);

        // 分块上传(大文件友好)
        $chunkSizeBytes = 1 * 1024 * 1024;
        $this->youtube->getClient()->setDefer(true);
        $insertRequest = $this->youtube->videos->insert('status,snippet', $video);

        $media = new MediaFileUpload(
            $this->youtube->getClient(),
            $insertRequest,
            'video/*',
            null,
            true,
            $chunkSizeBytes
        );
        $media->setFileSize(filesize($filePath));

        $uploadStatus = false;
        $handle = fopen($filePath, 'rb');
        while (!$uploadStatus && !feof($handle)) {
            $chunk = fread($handle, $chunkSizeBytes);
            $uploadStatus = $media->nextChunk($chunk);
        }
        fclose($handle);
        $this->youtube->getClient()->setDefer(false);

        return $uploadStatus;
    }
}

3. 创建上传表单视图

编写前端上传表单:

<!-- resources/views/videos/upload.blade.php -->
<form action="{{ route('videos.upload') }}" method="POST" enctype="multipart/form-data">
    @csrf
    <div class="form-group">
        <label>视频标题</label>
        <input type="text" name="title" class="form-control" required>
    </div>
    <div class="form-group">
        <label>视频描述</label>
        <textarea name="description" class="form-control" rows="3" required></textarea>
    </div>
    <div class="form-group">
        <label>视频文件</label>
        <input type="file" name="video" class="form-control-file" accept="video/*" required>
        <small class="text-muted">支持MP4/MOV/AVI格式,最大100MB</small>
    </div>
    <button type="submit" class="btn btn-primary">上传至YouTube</button>
    @if(session('success'))
        <div class="alert alert-success mt-3">{{ session('success') }}</div>
    @endif
</form>

4. 配置路由与控制器逻辑

添加路由到routes/web.php:

use App\Http\Controllers\VideoController;

Route::get('/videos/upload', [VideoController::class, 'showUploadForm'])->name('videos.upload.form');
Route::post('/videos/upload', [VideoController::class, 'uploadToYouTube'])->name('videos.upload');

创建控制器处理上传请求:

// app/Http/Controllers/VideoController.php
namespace App\Http\Controllers;

use App\Models\Video;
use App\Services\YouTubeService;
use Illuminate\Http\Request;

class VideoController extends Controller
{
    public function showUploadForm()
    {
        return view('videos.upload');
    }

    public function uploadToYouTube(Request $request, YouTubeService $youtubeService)
    {
        $validated = $request->validate([
            'title' => 'required|string|max:255',
            'description' => 'required|string',
            'video' => 'required|file|mimes:mp4,mov,avi|max:102400',
        ]);

        // 临时存储上传文件
        $videoFile = $request->file('video');
        $tempPath = $videoFile->getRealPath();

        // 上传到YouTube
        $youtubeVideo = $youtubeService->uploadVideo(
            $tempPath,
            $validated['title'],
            $validated['description']
        );

        // 拼接视频URL
        $videoUrl = "https://www.youtube.com/watch?v={$youtubeVideo->id}";

        // 保存到数据库
        Video::create([
            'title' => $validated['title'],
            'description' => $validated['description'],
            'youtube_url' => $videoUrl,
            'youtube_video_id' => $youtubeVideo->id,
        ]);

        return redirect()->back()->with('success', '视频上传成功,已同步至数据库!');
    }
}

5. 生成数据库模型与迁移

创建视频模型和数据表:

php artisan make:model Video -m

编辑迁移文件:

// database/migrations/xxxx_xx_xx_xxxxxx_create_videos_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up()
    {
        Schema::create('videos', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('description')->nullable();
            $table->string('youtube_url');
            $table->string('youtube_video_id')->unique();
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('videos');
    }
};

运行迁移:

php artisan migrate

关键注意事项

  • API配额:上传视频会消耗YouTube API配额,需确保配额充足
  • 文件限制:调整PHP配置upload_max_filesize和post_max_size以支持大文件上传
  • 令牌有效期:刷新令牌长期有效,但如果授权被撤销需重新获取
  • 隐私设置:可修改uploadVideo方法的privacyStatus参数,支持public/private/unlisted三种状态

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 18:01:11