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

如何基于Go+GIN实现带起止时间参数的视频片段服务?

实现Go+GIN+MinIO的视频片段请求服务

问题描述

我正尝试使用Go+GIN后端从MinIO存储中提供视频服务,目前基础功能已实现,但需要添加支持起止时间参数的视频片段请求功能。例如,对于一段10分钟的视频,我希望能请求其中2到3分钟的片段。请问是否可行?是否有相关示例参考?

当前实现代码:

accessKeyID := ""
secretAccessKey := ""
useSSL := false

ctx := context.Background()
endpoint := "127.0.0.1:9000"
bucketName := "mybucket"

// Initialize minio client object.
minioClient, err := minio.New(endpoint, &minio.Options{
    Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
    Secure: useSSL,
})
if err != nil {
    log.Fatalln(err)
}

// Get file
object, err := minioClient.GetObject(ctx, bucketName, "1.mp4", minio.GetObjectOptions{})
if err != nil {
    fmt.Println(err)
    return
}
objInfo, err := object.Stat()
if err != nil {
    return
}

buffer := make([]byte, objInfo.Size)
object.Read(buffer)

c.Writer.Header().Set("Content-Length", fmt.Sprintf("%d", objInfo.Size))
c.Writer.Header().Set("Content-Type", "video/mp4")
c.Writer.Header().Set("Connection", "keep-alive")
c.Writer.Header().Set("Content-Range", fmt.Sprintf("bytes 0-%d/%d", objInfo.Size, objInfo.Size))

//c.Writer.Write(buffer)
c.DataFromReader(200, objInfo.Size, "video/mp4", bytes.NewReader(buffer), nil)

解决方案

完全可行,核心思路是解析起止时间参数并转换为字节范围,通过MinIO的范围读取功能获取对应片段,同时设置正确的HTTP响应头让客户端识别片段内容。

关键实现步骤

  1. 接收请求参数:从GIN的Query参数中获取start和end(单位建议用秒,便于计算)。
  2. 时间转字节范围:基于视频码率计算对应时间点的字节偏移量(生产环境建议解析视频元数据获取精确码率)。
  3. MinIO范围读取:通过GetObjectOptions.SetRange指定读取的字节区间,避免加载整个视频到内存。
  4. 设置响应头:返回206 Partial Content状态码,配置Content-Range、Content-Length等必要头信息。

修改后的完整代码

import (
    "context"
    "fmt"
    "log"
    "net/http"
    "strconv"
    "github.com/gin-gonic/gin"
    "github.com/minio/minio-go/v7"
    "github.com/minio/minio-go/v7/pkg/credentials"
)

func serveVideoFragment(c *gin.Context) {
    accessKeyID := ""
    secretAccessKey := ""
    useSSL := false

    ctx := context.Background()
    endpoint := "127.0.0.1:9000"
    bucketName := "mybucket"
    objectName := "1.mp4"

    // 初始化MinIO客户端
    minioClient, err := minio.New(endpoint, &minio.Options{
        Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
        Secure: useSSL,
    })
    if err != nil {
        log.Fatalln(err)
        c.AbortWithStatus(http.StatusInternalServerError)
        return
    }

    // 获取视频对象元信息
    objInfo, err := minioClient.StatObject(ctx, bucketName, objectName, minio.StatObjectOptions{})
    if err != nil {
        fmt.Println(err)
        c.AbortWithStatus(http.StatusNotFound)
        return
    }

    // 解析起止时间参数(单位:秒)
    startStr := c.Query("start")
    endStr := c.Query("end")
    var startByte, endByte int64 = 0, objInfo.Size - 1

    if startStr != "" && endStr != "" {
        startSec, err := strconv.ParseInt(startStr, 10, 64)
        if err != nil {
            c.AbortWithStatus(http.StatusBadRequest)
            return
        }
        endSec, err := strconv.ParseInt(endStr, 10, 64)
        if err != nil {
            c.AbortWithStatus(http.StatusBadRequest)
            return
        }

        // 示例:假设视频码率为1MB/s(生产环境需解析视频元数据获取精确值)
        bitrate := int64(1024 * 1024)
        startByte = startSec * bitrate
        endByte = endSec * bitrate

        // 修正超出文件范围的字节值
        if endByte >= objInfo.Size {
            endByte = objInfo.Size - 1
        }
        if startByte >= objInfo.Size {
            c.AbortWithStatus(http.StatusRequestedRangeNotSatisfiable)
            return
        }
    }

    // 设置MinIO范围读取选项
    opts := minio.GetObjectOptions{}
    opts.SetRange(startByte, endByte)

    // 获取指定范围的视频流
    object, err := minioClient.GetObject(ctx, bucketName, objectName, opts)
    if err != nil {
        fmt.Println(err)
        c.AbortWithStatus(http.StatusInternalServerError)
        return
    }
    defer object.Close()

    // 计算片段长度
    contentLength := endByte - startByte + 1

    // 设置响应头
    c.Header("Content-Type", "video/mp4")
    c.Header("Content-Length", fmt.Sprintf("%d", contentLength))
    c.Header("Content-Range", fmt.Sprintf("bytes %d-%d/%d", startByte, endByte, objInfo.Size))
    c.Header("Accept-Ranges", "bytes")

    // 返回部分内容响应
    c.DataFromReader(http.StatusPartialContent, contentLength, "video/mp4", object, nil)
}

注意事项

  • 码率准确性:示例中用固定码率是简化处理,生产环境建议用ffprobe或Go视频处理库(如github.com/3d0c/gmf)解析视频元数据,获取精确的码率和时间戳对应关系,避免片段偏移误差。
  • 内存优化:直接传递MinIO返回的流给DataFromReader,不要像原代码那样把整个视频读入内存,降低服务内存占用。
  • 客户端兼容性:现代浏览器和主流视频播放器都支持Range请求和206 Partial Content响应,无需额外兼容处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 06:40:22