Files
LitePan/internal/api/upload.go
T
2026-07-27 19:31:43 +08:00

180 lines
5.2 KiB
Go

package api
import (
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/go-chi/chi/v5"
"litepan/internal/domain"
"litepan/internal/upload"
)
type batchDeleteUploadTasksReq struct {
TaskIDs []string `json:"task_ids"`
DeleteUploadedFile bool `json:"delete_uploaded_file"`
}
func (h *Handler) createUploadTask(w http.ResponseWriter, r *http.Request) {
if err := r.ParseMultipartForm(32 << 20); err != nil {
writeErr(w, domain.Errorf(domain.CodeValidation, "解析上传表单失败"))
return
}
accountID, err := strconv.ParseInt(r.FormValue("account_id"), 10, 64)
if err != nil || accountID <= 0 {
writeErr(w, domain.Errorf(domain.CodeValidation, "非法 account_id"))
return
}
targetPath := r.FormValue("path")
conflictPolicy := r.FormValue("conflict_policy")
if conflictPolicy == "" {
conflictPolicy = "overwrite"
}
file, header, err := r.FormFile("file")
if err != nil {
writeErr(w, domain.Errorf(domain.CodeValidation, "缺少上传文件"))
return
}
defer file.Close()
tempPath, total, err := saveUploadTemp(h.uploads.TempDir(), file, header.Filename)
if err != nil {
writeErr(w, err)
return
}
if total < 0 {
_ = os.Remove(tempPath)
writeErr(w, domain.Errorf(domain.CodeValidation, "上传文件大小非法"))
return
}
displayName := strings.TrimSpace(r.FormValue("display_name"))
fileName := header.Filename
if fileName == "" {
fileName = filepath.Base(tempPath)
}
task, err := h.uploads.Create(r.Context(), upload.CreateParams{
ClientTaskID: r.FormValue("client_task_id"),
AccountID: accountID,
FileName: fileName,
DisplayName: displayName,
TargetPath: targetPath,
TargetDisplayPath: r.FormValue("target_display_path"),
LocalPath: tempPath,
TotalBytes: total,
ConflictPolicy: conflictPolicy,
})
if err != nil {
_ = os.Remove(tempPath)
writeErr(w, err)
return
}
writeJSON(w, http.StatusOK, Resp{Success: true, Message: "上传任务已创建", Data: task})
}
func saveUploadTemp(dir string, src io.Reader, name string) (string, int64, error) {
if err := os.MkdirAll(dir, 0o755); err != nil {
return "", 0, domain.Wrap(domain.CodeInternal, err)
}
ext := filepath.Ext(name)
path := filepath.Join(dir, fmt.Sprintf("%d%s", time.Now().UnixNano(), ext))
out, err := os.Create(path)
if err != nil {
return "", 0, domain.Wrap(domain.CodeInternal, err)
}
defer out.Close()
n, err := io.Copy(out, src)
if err != nil {
_ = os.Remove(path)
return "", 0, domain.Wrap(domain.CodeInternal, err)
}
return path, n, nil
}
func (h *Handler) listUploadTasks(w http.ResponseWriter, r *http.Request) {
var accountID int64
if raw := r.URL.Query().Get("account_id"); raw != "" {
id, err := strconv.ParseInt(raw, 10, 64)
if err != nil {
writeErr(w, domain.Errorf(domain.CodeValidation, "非法 account_id"))
return
}
accountID = id
}
tasks := h.uploads.List(r.Context(), accountID)
writeJSON(w, http.StatusOK, Resp{Success: true, Message: "获取上传任务成功", Data: tasks})
}
func (h *Handler) streamUploadTasks(w http.ResponseWriter, r *http.Request) {
s, err := newSSEWriter(w)
if err != nil {
writeErr(w, err)
return
}
ch := h.uploads.Subscribe()
defer h.uploads.Unsubscribe(ch)
streamSSEByteMessages(r, s, "tasks", h.uploads.SnapshotPayload(), ch)
}
func (h *Handler) getUploadTask(w http.ResponseWriter, r *http.Request) {
taskID := chi.URLParam(r, "taskID")
task, ok := h.uploads.Get(r.Context(), taskID)
if !ok {
writeErr(w, domain.Errorf(domain.CodeNotFound, "上传任务不存在"))
return
}
writeJSON(w, http.StatusOK, Resp{Success: true, Message: "获取上传任务成功", Data: task})
}
func (h *Handler) pauseUploadTask(w http.ResponseWriter, r *http.Request) {
taskID := chi.URLParam(r, "taskID")
task, ok := h.uploads.Pause(r.Context(), taskID)
if !ok {
writeErr(w, domain.Errorf(domain.CodeNotFound, "上传任务不存在"))
return
}
writeJSON(w, http.StatusOK, Resp{Success: true, Message: "上传任务已暂停", Data: task})
}
func (h *Handler) resumeUploadTask(w http.ResponseWriter, r *http.Request) {
taskID := chi.URLParam(r, "taskID")
task, ok := h.uploads.Resume(r.Context(), taskID)
if !ok {
writeErr(w, domain.Errorf(domain.CodeNotFound, "上传任务不存在"))
return
}
writeJSON(w, http.StatusOK, Resp{Success: true, Message: "上传任务已继续", Data: task})
}
func (h *Handler) deleteUploadTask(w http.ResponseWriter, r *http.Request) {
taskID := chi.URLParam(r, "taskID")
deleteFile := r.URL.Query().Get("delete_uploaded_file") == "true"
found, err := h.uploads.Delete(r.Context(), taskID, deleteFile)
if !found {
writeErr(w, domain.Errorf(domain.CodeNotFound, "上传任务不存在"))
return
}
if err != nil {
writeErr(w, err)
return
}
writeJSON(w, http.StatusOK, Resp{Success: true, Message: "上传任务已删除"})
}
func (h *Handler) batchDeleteUploadTasks(w http.ResponseWriter, r *http.Request) {
var req batchDeleteUploadTasksReq
if err := decodeJSON(r, &req); err != nil {
writeErr(w, err)
return
}
result := h.uploads.BatchDelete(r.Context(), req.TaskIDs, req.DeleteUploadedFile)
writeJSON(w, http.StatusOK, Resp{Success: true, Message: "批量删除上传任务成功", Data: result})
}