mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-09 23:17:21 +08:00
feat: 文件上传权限优化 (#4441)
Refs https://github.com/1Panel-dev/1Panel/issues/4362
This commit is contained in:
parent
0751326b46
commit
1e22769c8b
2 changed files with 82 additions and 27 deletions
|
@ -11,7 +11,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||||
|
@ -301,9 +301,9 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
files := form.File["file"]
|
uploadFiles := form.File["file"]
|
||||||
paths := form.Value["path"]
|
paths := form.Value["path"]
|
||||||
|
|
||||||
overwrite := true
|
overwrite := true
|
||||||
if ow, ok := form.Value["overwrite"]; ok {
|
if ow, ok := form.Value["overwrite"]; ok {
|
||||||
if len(ow) != 0 {
|
if len(ow) != 0 {
|
||||||
|
@ -311,22 +311,47 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
|
||||||
overwrite = parseBool
|
overwrite = parseBool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(paths) == 0 || !strings.Contains(paths[0], "/") {
|
if len(paths) == 0 || !strings.Contains(paths[0], "/") {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error paths in request"))
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error paths in request"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
dir := path.Dir(paths[0])
|
dir := path.Dir(paths[0])
|
||||||
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
|
|
||||||
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
|
info, err := os.Stat(dir)
|
||||||
|
if err != nil && os.IsNotExist(err) {
|
||||||
|
mode, err := files.GetParentMode(dir)
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = os.MkdirAll(dir, mode); err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, fmt.Errorf("mkdir %s failed, err: %v", dir, err))
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, fmt.Errorf("mkdir %s failed, err: %v", dir, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
info, err = os.Stat(dir)
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mode := info.Mode()
|
||||||
|
|
||||||
|
fileOp := files.NewFileOp()
|
||||||
|
|
||||||
success := 0
|
success := 0
|
||||||
failures := make(buserr.MultiErr)
|
failures := make(buserr.MultiErr)
|
||||||
for _, file := range files {
|
for _, file := range uploadFiles {
|
||||||
dstFilename := path.Join(paths[0], file.Filename)
|
dstFilename := path.Join(paths[0], file.Filename)
|
||||||
|
dstDir := path.Dir(dstFilename)
|
||||||
|
if !fileOp.Stat(dstDir) {
|
||||||
|
if err = fileOp.CreateDir(dstDir, mode); err != nil {
|
||||||
|
e := fmt.Errorf("create dir [%s] failed, err: %v", path.Dir(dstFilename), err)
|
||||||
|
failures[file.Filename] = e
|
||||||
|
global.LOG.Error(e)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
tmpFilename := dstFilename + ".tmp"
|
tmpFilename := dstFilename + ".tmp"
|
||||||
if err := c.SaveUploadedFile(file, tmpFilename); err != nil {
|
if err := c.SaveUploadedFile(file, tmpFilename); err != nil {
|
||||||
_ = os.Remove(tmpFilename)
|
_ = os.Remove(tmpFilename)
|
||||||
|
@ -335,11 +360,11 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
|
||||||
global.LOG.Error(e)
|
global.LOG.Error(e)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
stat, statErr := os.Stat(dstFilename)
|
dstInfo, statErr := os.Stat(dstFilename)
|
||||||
if overwrite {
|
if overwrite {
|
||||||
_ = os.Remove(dstFilename)
|
_ = os.Remove(dstFilename)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.Rename(tmpFilename, dstFilename)
|
err = os.Rename(tmpFilename, dstFilename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = os.Remove(tmpFilename)
|
_ = os.Remove(tmpFilename)
|
||||||
|
@ -349,7 +374,9 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if statErr == nil {
|
if statErr == nil {
|
||||||
_ = os.Chmod(dstFilename, stat.Mode())
|
_ = os.Chmod(dstFilename, dstInfo.Mode())
|
||||||
|
} else {
|
||||||
|
_ = os.Chmod(dstFilename, mode)
|
||||||
}
|
}
|
||||||
success++
|
success++
|
||||||
}
|
}
|
||||||
|
@ -499,29 +526,29 @@ func (b *BaseApi) DownloadChunkFiles(c *gin.Context) {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrFileDownloadDir, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrFileDownloadDir, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Writer.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", req.Name))
|
c.Writer.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", req.Name))
|
||||||
c.Writer.Header().Set("Content-Type", "application/octet-stream")
|
c.Writer.Header().Set("Content-Type", "application/octet-stream")
|
||||||
c.Writer.Header().Set("Content-Length", strconv.FormatInt(info.Size(), 10))
|
c.Writer.Header().Set("Content-Length", strconv.FormatInt(info.Size(), 10))
|
||||||
c.Writer.Header().Set("Accept-Ranges", "bytes")
|
c.Writer.Header().Set("Accept-Ranges", "bytes")
|
||||||
|
|
||||||
if c.Request.Header.Get("Range") != "" {
|
if c.Request.Header.Get("Range") != "" {
|
||||||
rangeHeader := c.Request.Header.Get("Range")
|
rangeHeader := c.Request.Header.Get("Range")
|
||||||
rangeArr := strings.Split(rangeHeader, "=")[1]
|
rangeArr := strings.Split(rangeHeader, "=")[1]
|
||||||
rangeParts := strings.Split(rangeArr, "-")
|
rangeParts := strings.Split(rangeArr, "-")
|
||||||
|
|
||||||
startPos, _ := strconv.ParseInt(rangeParts[0], 10, 64)
|
startPos, _ := strconv.ParseInt(rangeParts[0], 10, 64)
|
||||||
|
|
||||||
var endPos int64
|
var endPos int64
|
||||||
if rangeParts[1] == "" {
|
if rangeParts[1] == "" {
|
||||||
endPos = info.Size() - 1
|
endPos = info.Size() - 1
|
||||||
} else {
|
} else {
|
||||||
endPos, _ = strconv.ParseInt(rangeParts[1], 10, 64)
|
endPos, _ = strconv.ParseInt(rangeParts[1], 10, 64)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Writer.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", startPos, endPos, info.Size()))
|
c.Writer.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", startPos, endPos, info.Size()))
|
||||||
c.Writer.WriteHeader(http.StatusPartialContent)
|
c.Writer.WriteHeader(http.StatusPartialContent)
|
||||||
|
|
||||||
buffer := make([]byte, 1024*1024)
|
buffer := make([]byte, 1024*1024)
|
||||||
file, err := os.Open(filePath)
|
file, err := os.Open(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -529,7 +556,7 @@ func (b *BaseApi) DownloadChunkFiles(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
_, _ = file.Seek(startPos, 0)
|
_, _ = file.Seek(startPos, 0)
|
||||||
reader := io.LimitReader(file, endPos-startPos+1)
|
reader := io.LimitReader(file, endPos-startPos+1)
|
||||||
_, err = io.CopyBuffer(c.Writer, reader, buffer)
|
_, err = io.CopyBuffer(c.Writer, reader, buffer)
|
||||||
|
@ -567,17 +594,21 @@ func (b *BaseApi) Size(c *gin.Context) {
|
||||||
func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int) error {
|
func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int) error {
|
||||||
op := files.NewFileOp()
|
op := files.NewFileOp()
|
||||||
dstDir = strings.TrimSpace(dstDir)
|
dstDir = strings.TrimSpace(dstDir)
|
||||||
|
mode, _ := files.GetParentMode(dstDir)
|
||||||
|
if mode == 0 {
|
||||||
|
mode = os.ModePerm
|
||||||
|
}
|
||||||
if _, err := os.Stat(dstDir); err != nil && os.IsNotExist(err) {
|
if _, err := os.Stat(dstDir); err != nil && os.IsNotExist(err) {
|
||||||
if err = op.CreateDir(dstDir, os.ModePerm); err != nil {
|
if err = op.CreateDir(dstDir, mode); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
targetFile, err := os.Create(filepath.Join(dstDir, fileName))
|
|
||||||
|
targetFile, err := os.OpenFile(filepath.Join(dstDir, fileName), os.O_RDWR|os.O_CREATE, mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer targetFile.Close()
|
|
||||||
|
|
||||||
for i := 0; i < chunkCount; i++ {
|
for i := 0; i < chunkCount; i++ {
|
||||||
chunkPath := filepath.Join(fileDir, fmt.Sprintf("%s.%d", fileName, i))
|
chunkPath := filepath.Join(fileDir, fmt.Sprintf("%s.%d", fileName, i))
|
||||||
chunkData, err := os.ReadFile(chunkPath)
|
chunkData, err := os.ReadFile(chunkPath)
|
||||||
|
@ -589,7 +620,7 @@ func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return files.NewFileOp().DeleteDir(fileDir)
|
return files.NewFileOp().DeleteDir(fileDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -639,7 +670,7 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
|
||||||
_ = os.MkdirAll(fileDir, 0755)
|
_ = os.MkdirAll(fileDir, 0755)
|
||||||
}
|
}
|
||||||
filePath := filepath.Join(fileDir, filename)
|
filePath := filepath.Join(fileDir, filename)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = os.Remove(fileDir)
|
_ = os.Remove(fileDir)
|
||||||
|
@ -649,27 +680,27 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
|
||||||
emptyFile *os.File
|
emptyFile *os.File
|
||||||
chunkData []byte
|
chunkData []byte
|
||||||
)
|
)
|
||||||
|
|
||||||
emptyFile, err = os.Create(filePath)
|
emptyFile, err = os.Create(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer emptyFile.Close()
|
defer emptyFile.Close()
|
||||||
|
|
||||||
chunkData, err = io.ReadAll(uploadFile)
|
chunkData, err = io.ReadAll(uploadFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, buserr.WithMap(constant.ErrFileUpload, map[string]interface{}{"name": filename, "detail": err.Error()}, err))
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, buserr.WithMap(constant.ErrFileUpload, map[string]interface{}{"name": filename, "detail": err.Error()}, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
chunkPath := filepath.Join(fileDir, fmt.Sprintf("%s.%d", filename, chunkIndex))
|
chunkPath := filepath.Join(fileDir, fmt.Sprintf("%s.%d", filename, chunkIndex))
|
||||||
err = os.WriteFile(chunkPath, chunkData, 0644)
|
err = os.WriteFile(chunkPath, chunkData, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, buserr.WithMap(constant.ErrFileUpload, map[string]interface{}{"name": filename, "detail": err.Error()}, err))
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, buserr.WithMap(constant.ErrFileUpload, map[string]interface{}{"name": filename, "detail": err.Error()}, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if chunkIndex+1 == chunkCount {
|
if chunkIndex+1 == chunkCount {
|
||||||
err = mergeChunks(filename, fileDir, c.PostForm("path"), chunkCount)
|
err = mergeChunks(filename, fileDir, c.PostForm("path"), chunkCount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package files
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"fmt"
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -117,3 +118,26 @@ func ReadFileByLine(filename string, page, pageSize int) ([]string, bool, error)
|
||||||
|
|
||||||
return lines, isEndOfFile, nil
|
return lines, isEndOfFile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetParentMode(path string) (os.FileMode, error) {
|
||||||
|
absPath, err := filepath.Abs(path)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
fileInfo, err := os.Stat(absPath)
|
||||||
|
if err == nil {
|
||||||
|
return fileInfo.Mode(), nil
|
||||||
|
}
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
parentDir := filepath.Dir(absPath)
|
||||||
|
if parentDir == absPath {
|
||||||
|
return 0, fmt.Errorf("no existing directory found in the path: %s", path)
|
||||||
|
}
|
||||||
|
absPath = parentDir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue