119 lines
2.5 KiB
Go
119 lines
2.5 KiB
Go
package http
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"mime/multipart"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"quyun/pkg/utils"
|
|
"quyun/providers/app"
|
|
|
|
"github.com/gofiber/fiber/v3"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// @provider
|
|
type uploads struct {
|
|
app *app.Config
|
|
}
|
|
|
|
func (up *uploads) storagePath() string {
|
|
return filepath.Join(up.app.StoragePath, "uploads")
|
|
}
|
|
|
|
type UploadChunk struct {
|
|
Chunk int `query:"chunk"`
|
|
Md5 string `query:"md5"`
|
|
}
|
|
|
|
type UploadFileInfo struct {
|
|
Md5 string `json:"md5"`
|
|
Filename string `json:"filename"`
|
|
Mime string `json:"mime"`
|
|
Chunks int `json:"chunks"`
|
|
}
|
|
|
|
// Upload chunks
|
|
// @Router /v1/uploads/:md5/chunks/:idx [post]
|
|
// @Bind md5 path
|
|
// @Bind idx path
|
|
// @Bind file file
|
|
func (up *uploads) Chunks(ctx fiber.Ctx, md5, idx string, file *multipart.FileHeader) error {
|
|
tmpPath := filepath.Join(up.storagePath(), md5, idx)
|
|
|
|
// if tmpPath not exists, create it
|
|
if _, err := os.Stat(tmpPath); os.IsNotExist(err) {
|
|
if err := os.MkdirAll(filepath.Dir(tmpPath), os.ModePerm); err != nil {
|
|
log.WithError(err).Errorf("create tmpPath failed %s", tmpPath)
|
|
return err
|
|
}
|
|
}
|
|
|
|
// save file to tmpPath
|
|
if err := ctx.SaveFile(file, tmpPath); err != nil {
|
|
log.WithError(err).Errorf("save file to tmpPath failed %s", tmpPath)
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Complete uploads
|
|
// @Router /v1/uploads/:md5/complete [post]
|
|
// @Bind md5 path
|
|
// @Bind body body
|
|
func (up *uploads) Complete(ctx fiber.Ctx, md5 string, body *UploadFileInfo) error {
|
|
// merge chunks
|
|
path := filepath.Join(up.storagePath(), md5)
|
|
defer os.RemoveAll(path)
|
|
|
|
targetFile := filepath.Join(up.storagePath(), md5, body.Filename)
|
|
|
|
// if targetFile not exists, create it
|
|
tf, err := os.Create(targetFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for i := 0; i < body.Chunks; i++ {
|
|
tmpPath := filepath.Join(up.storagePath(), md5, fmt.Sprintf("%d", i))
|
|
|
|
// open chunk file
|
|
chunkFile, err := os.Open(tmpPath)
|
|
if err != nil {
|
|
tf.Close()
|
|
return err
|
|
}
|
|
|
|
// copy chunk file to target file
|
|
if _, err := tf.ReadFrom(chunkFile); err != nil {
|
|
chunkFile.Close()
|
|
tf.Close()
|
|
return err
|
|
}
|
|
|
|
chunkFile.Close()
|
|
}
|
|
tf.Close()
|
|
|
|
// validate md5
|
|
ok, err := utils.CompareFileMd5(targetFile, md5)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !ok {
|
|
return errors.New("md5 not match")
|
|
}
|
|
|
|
// save file to target path
|
|
targetPath := filepath.Join(up.storagePath(), body.Filename)
|
|
|
|
if err := os.Rename(targetFile, targetPath); err != nil {
|
|
return err
|
|
}
|
|
// TODO: save file to database
|
|
|
|
return nil
|
|
}
|