feat: 分片上传断点续传、临时目录后台配置与清扫、宇恒云账号管理
- 管理端大文件分片上传与 sessionStorage 续传;Nginx 大请求体/超时 - .chunk-uploads 定期清扫;system_config 后台配置保留时长与扫描间隔 - 宇恒云 POST /register 对接与 yuheng_cloud_register_records 留痕;yuheng_cloud:manage 权限 Made-with: Cursor
This commit is contained in:
@@ -207,6 +207,42 @@ func ServePromotionMedia(c *gin.Context) {
|
||||
c.File(fullPath)
|
||||
}
|
||||
|
||||
// computeSiteUploadDest 与整文件上传、分片合并完成时一致的目标路径(非 preserve 时 saveName 含当前时间戳)
|
||||
func computeSiteUploadDest(siteID, folder, originalFilename string, preserve bool) (relPath, destPath string, errMsg string) {
|
||||
name := originalFilename
|
||||
ext := filepath.Ext(name)
|
||||
nameNoExt := strings.TrimSuffix(name, ext)
|
||||
var saveName string
|
||||
if preserve {
|
||||
saveName = filepath.Base(name)
|
||||
if saveName == "." || saveName == string(filepath.Separator) || saveName == "" {
|
||||
return "", "", "无效的文件名"
|
||||
}
|
||||
} else {
|
||||
if len(ext) == 0 {
|
||||
saveName = nameNoExt + "_" + time.Now().Format("20060102150405")
|
||||
} else {
|
||||
saveName = nameNoExt + "_" + time.Now().Format("20060102150405") + ext
|
||||
}
|
||||
}
|
||||
|
||||
folderClean := ""
|
||||
if folder != "" {
|
||||
folderClean = filepath.ToSlash(filepath.Clean(folder))
|
||||
if strings.HasPrefix(folderClean, "../") || strings.Contains(folderClean, "/../") {
|
||||
return "", "", "无效的目录路径"
|
||||
}
|
||||
}
|
||||
|
||||
if folderClean != "" {
|
||||
relPath = filepath.ToSlash(filepath.Join("sites", siteID, folderClean, saveName))
|
||||
} else {
|
||||
relPath = filepath.ToSlash(filepath.Join("sites", siteID, saveName))
|
||||
}
|
||||
destPath = filepath.Join(getUploadDir(), filepath.FromSlash(relPath))
|
||||
return relPath, destPath, ""
|
||||
}
|
||||
|
||||
// UploadSiteAsset 上传功能模块/文件;form 可选:folder(当前目录相对路径)、downloadable(true/false)、preserve_filename(true 时保持原名并覆盖同路径旧文件,用于首页推广视频固定 URL)
|
||||
func UploadSiteAsset(c *gin.Context) {
|
||||
siteID := c.Param("site_id")
|
||||
@@ -225,41 +261,12 @@ func UploadSiteAsset(c *gin.Context) {
|
||||
downloadable := c.PostForm("downloadable") == "true" || c.PostForm("downloadable") == "1"
|
||||
preserve := c.PostForm("preserve_filename") == "true" || c.PostForm("preserve_filename") == "1"
|
||||
|
||||
name := file.Filename
|
||||
ext := filepath.Ext(name)
|
||||
nameNoExt := strings.TrimSuffix(name, ext)
|
||||
var saveName string
|
||||
if preserve {
|
||||
saveName = filepath.Base(name)
|
||||
if saveName == "." || saveName == string(filepath.Separator) || saveName == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的文件名"})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if len(ext) == 0 {
|
||||
saveName = nameNoExt + "_" + time.Now().Format("20060102150405")
|
||||
} else {
|
||||
saveName = nameNoExt + "_" + time.Now().Format("20060102150405") + ext
|
||||
}
|
||||
relPath, destPath, errMsg := computeSiteUploadDest(siteID, folder, file.Filename, preserve)
|
||||
if errMsg != "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": errMsg})
|
||||
return
|
||||
}
|
||||
|
||||
folderClean := ""
|
||||
if folder != "" {
|
||||
folderClean = filepath.ToSlash(filepath.Clean(folder))
|
||||
if strings.HasPrefix(folderClean, "../") || strings.Contains(folderClean, "/../") {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的目录路径"})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var relPath string
|
||||
if folderClean != "" {
|
||||
relPath = filepath.ToSlash(filepath.Join("sites", siteID, folderClean, saveName))
|
||||
} else {
|
||||
relPath = filepath.ToSlash(filepath.Join("sites", siteID, saveName))
|
||||
}
|
||||
destPath := filepath.Join(getUploadDir(), filepath.FromSlash(relPath))
|
||||
|
||||
if preserve {
|
||||
ctxDel, cancelDel := context.WithTimeout(context.Background(), 8*time.Second)
|
||||
defer cancelDel()
|
||||
|
||||
Reference in New Issue
Block a user