Learn.VideoAnalysis/VideoAnalysisCore/Common/Expand/UploadExpand.cs

160 lines
6.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using AlibabaCloud.SDK.Vod20170321;
using AlibabaCloud.SDK.Vod20170321.Models;
using Aliyun.OSS;
using Microsoft.Extensions.DependencyInjection;
using SqlSugar.IOC;
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using VideoAnalysisCore.Model;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
namespace VideoAnalysisCore.Common.Expand
{
public static class UploadExpand
{
public static void AddUploadExpand(this IServiceCollection services)
{
services.AddSingleton<UploadHandle>();
}
}
public class UploadHandle
{
private readonly Client _vodClient;
private readonly Repository<VideoTask> _videoTaskDB;
private readonly RedisManager _redisManager;
private readonly OssClient _ossClient; // 使用系统统一注入的 OSS Client
public UploadHandle(Client vodClient, Repository<VideoTask> videoTaskDB, RedisManager redisManager, OssClient ossClient)
{
_vodClient = vodClient;
_videoTaskDB = videoTaskDB;
_redisManager = redisManager;
_ossClient = ossClient;
}
public async Task RunAsync(string task)
{
var taskId = long.Parse(task);
var localPath = task.LocalPath();
var m3u8Path = Path.Combine(localPath, "out.m3u8");
if (!File.Exists(m3u8Path))
{
await _redisManager.AddTaskLog(task, "未找到 m3u8 文件,无法进行切片上传");
throw new FileNotFoundException("M3U8文件未找到", m3u8Path);
}
// 获取所有切片文件 (out*.ts)
var tsFiles = Directory.GetFiles(localPath, "out*.ts");
if (tsFiles.Length == 0)
{
await _redisManager.AddTaskLog(task, "未找到 ts 切片文件");
throw new FileNotFoundException("TS切片文件未找到");
}
var title = $"Task_{taskId}_{DateTime.Now:yyyyMMddHHmmss}";
await _redisManager.AddTaskLog(task, "正在获取VOD上传凭证...");
// 1. 获取上传凭证和地址
// 注意VOD上传m3u8时FileName必须以 .m3u8 结尾
var request = new CreateUploadVideoRequest
{
Title = title,
FileName = "out.m3u8", // 必须是 m3u8 文件名
Description = "Video Analysis HLS Upload",
// CoverURL = "...", // 可选:设置封面
// Tags = "...", // 可选:设置标签
};
var response = await _vodClient.CreateUploadVideoAsync(request);
if (response.Body == null || string.IsNullOrEmpty(response.Body.UploadAddress) || string.IsNullOrEmpty(response.Body.UploadAuth))
{
throw new Exception($"获取上传凭证失败: RequestId={response.Body?.RequestId}");
}
var videoId = response.Body.VideoId;
var uploadAddressStr = response.Body.UploadAddress;
var uploadAuthStr = response.Body.UploadAuth;
await _redisManager.AddTaskLog(task, $"获取凭证成功VideoId: {videoId}");
// 2. 解析凭证 (Base64 -> JSON)
var addressJson = JObject.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(uploadAddressStr)));
var authJson = JObject.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(uploadAuthStr)));
var endpoint = addressJson["Endpoint"]?.ToString();
var bucket = addressJson["Bucket"]?.ToString();
var objectName = addressJson["FileName"]?.ToString(); // 这是 VOD 分配的 m3u8 存储路径,例如 "sv/243d.../out.m3u8"
var accessKeyId = authJson["AccessKeyId"]?.ToString();
var accessKeySecret = authJson["AccessKeySecret"]?.ToString();
var securityToken = authJson["SecurityToken"]?.ToString();
if (string.IsNullOrEmpty(endpoint) || string.IsNullOrEmpty(bucket) || string.IsNullOrEmpty(objectName))
{
throw new Exception("解析上传地址失败");
}
// 修正 Endpoint 格式 (如果缺少协议头)
if (!endpoint.StartsWith("http"))
{
endpoint = "https://" + endpoint;
}
// 3. 构造 OSS 客户端 (使用临时凭证)
var ossClient = new OssClient(endpoint, accessKeyId, accessKeySecret, securityToken);
// 4. 确定 OSS 目录前缀
// VOD 返回的 objectName 是完整的文件路径,我们需要提取目录部分来存放 .ts 文件
// 例如: objectName = "sv/5903240e-19544975a64/out.m3u8"
// 则 prefix = "sv/5903240e-19544975a64/"
var ossPrefix = objectName.Substring(0, objectName.LastIndexOf('/') + 1);
await _redisManager.AddTaskLog(task, $"开始上传文件到 VOD OSS (Bucket: {bucket}, Prefix: {ossPrefix})...");
try
{
// A. 上传所有 TS 切片
await _redisManager.AddTaskLog(task, $"开始上传 TS 切片 (共 {tsFiles.Length} 个)...");
foreach (var tsFile in tsFiles)
{
var fileName = Path.GetFileName(tsFile);
var tsObjectKey = ossPrefix + fileName;
using var fs = File.OpenRead(tsFile);
ossClient.PutObject(bucket, tsObjectKey, fs);
}
// B. 上传 m3u8 索引文件
// 必须使用 VOD 指定的 objectName
await _redisManager.AddTaskLog(task, "开始上传 m3u8 索引文件...");
using (var fs = File.OpenRead(m3u8Path))
{
ossClient.PutObject(bucket, objectName, fs);
}
await _redisManager.AddTaskLog(task, "上传成功");
// 5. 更新数据库
// 对于 VOD 托管视频,我们主要存储 VideoId (TagId),播放地址通常由前端调用 VOD 接口获取
// 或者我们可以尝试获取播放地址存入 MediaUrl
await _videoTaskDB.CopyNew().AsUpdateable()
.SetColumns(it => it.TagId == videoId)
.Where(it => it.Id == taskId)
.ExecuteCommandAsync();
}
catch (Exception ex)
{
await _redisManager.AddTaskLog(task, $"上传 VOD OSS 异常: {ex.Message}");
throw;
}
}
}
}