134 lines
4.2 KiB
Go
134 lines
4.2 KiB
Go
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
|
||
}
|