EasyAudioEncode/internal/web/api/update.go
2025-12-31 11:29:58 +08:00

134 lines
4.2 KiB
Go
Raw Permalink 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.

package api
import (
"easyaudioencode/internal/core/audioencode"
"easyaudioencode/pkg/ffmpeg"
"fmt"
"git.lnton.com/lnton/pkg/reason"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"io"
"mime/multipart"
"net/http"
"os"
"path/filepath"
"strings"
)
// 配置常量
const (
// 最大上传文件大小 100MB
maxUploadSize = 100 * 1024 * 1024
// 文件保存目录
uploadDir = "./uploads"
sourceDir = "source"
encodeDir = "encode"
)
var (
// 允许的文件扩展名
allowedExts = map[string]bool{
".mp3": true,
".wav": true,
}
// 允许的 MIME 类型
allowedMimeTypes = map[string]bool{
"audio/mpeg": true, // MP3
"audio/wav": true, // WAV
"audio/x-wav": true, // WAV 另一种常见 MIME
"audio/x-pn-wav": true,
}
)
// uploadAudioHandler 处理音频文件上传
func (a AudioEncodeAPI) uploadAudioHandler(c *gin.Context, _ *struct{}) (any, error) {
if err := os.MkdirAll(filepath.Join(uploadDir, sourceDir), 0755); err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf("创建上传目录失败: %v", err.Error()))
}
if err := os.MkdirAll(filepath.Join(uploadDir, encodeDir), 0755); err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf("创建转码目录失败: %v", err.Error()))
}
// 限制请求体大小
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, maxUploadSize)
if err := c.Request.ParseMultipartForm(maxUploadSize); err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf("文件过大(最大 %dMB或解析失败: %v", maxUploadSize/1024/1024, err))
}
// 获取上传的文件
file, fileHeader, err := c.Request.FormFile("audio")
if err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf("获取文件失败: %v", err))
}
defer file.Close()
// 1. 校验文件扩展名
fileExt := strings.ToLower(filepath.Ext(fileHeader.Filename))
sourceFileName := strings.TrimSuffix(fileHeader.Filename, fileExt)
if !allowedExts[fileExt] {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf("不支持的文件类型,仅允许 MP3/WAV当前扩展名: %s", fileExt))
}
// 2. 校验文件 MIME 类型
//mimeType, err := getFileMimeType(file)
//if err != nil {
// return nil, reason.ErrServer.SetMsg(fmt.Sprintf("获取文件 MIME 类型失败: %v", err))
//}
//if !allowedMimeTypes[mimeType] {
// return nil, reason.ErrServer.SetMsg(fmt.Sprintf("文件类型校验失败,实际 MIME: %s仅允许 MP3/WAV", mimeType))
//}
uuidStr := uuid.New().String()
// 生成唯一文件名(避免覆盖)
fileName := fmt.Sprintf("%s%s", uuidStr, fileExt)
filePath := filepath.Join(uploadDir, sourceDir, fileName)
outputFile := filepath.Join(uploadDir, encodeDir, fmt.Sprintf("%s.g711a", uuidStr))
// 创建目标文件
dstFile, err := os.Create(filePath)
if err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf("创建文件失败: %v", err))
}
defer dstFile.Close()
// 复制文件内容
if _, err := io.Copy(dstFile, file); err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf("保存文件失败: %v", err.Error()))
}
in := &audioencode.AddAudioEncodeInput{
Name: sourceFileName,
FileName: fileName,
Size: fileHeader.Size,
SourceUrl: filePath,
EncodeUrl: outputFile,
Mode: fileExt,
EncodeStatus: audioencode.EncodeStatusPing,
}
duration, _, err := ffmpeg.GetAudioDuration(filePath)
if err == nil {
in.Duration = int64(duration.Seconds())
}
info, err := a.core.AddAudioEncode(c.Request.Context(), in)
if err != nil {
return nil, reason.ErrServer.SetMsg(fmt.Sprintf(`add audioencode err [%s]`, err.Error()))
}
a.transcodeCore.StartAudioEncode(filePath, outputFile, info.ID)
return gin.H{"data": "上传成功!", "filePath": filePath, "filename": fileName, "size": fileHeader.Size}, err
}
// getFileMimeType 获取文件的真实 MIME 类型(通过读取文件前几个字节)
func getFileMimeType(file multipart.File) (string, error) {
// 读取文件前 512 字节用于检测 MIME 类型
buffer := make([]byte, 512)
n, err := file.Read(buffer)
if err != nil && err != io.EOF {
return "", err
}
// 重置文件指针到开头
if _, err := file.Seek(0, io.SeekStart); err != nil {
return "", err
}
// 检测 MIME 类型
return http.DetectContentType(buffer[:n]), nil
}