Skip to content

Commit

Permalink
feat: 文件上传权限优化 (#4441)
Browse files Browse the repository at this point in the history
Refs #4362
  • Loading branch information
zhengkunwang223 authored Apr 9, 2024
1 parent 0751326 commit 1e22769
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 27 deletions.
85 changes: 58 additions & 27 deletions backend/app/api/v1/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"path/filepath"
"strconv"
"strings"

"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/request"
Expand Down Expand Up @@ -301,32 +301,57 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
files := form.File["file"]
uploadFiles := form.File["file"]
paths := form.Value["path"]

overwrite := true
if ow, ok := form.Value["overwrite"]; ok {
if len(ow) != 0 {
parseBool, _ := strconv.ParseBool(ow[0])
overwrite = parseBool
}
}

if len(paths) == 0 || !strings.Contains(paths[0], "/") {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error paths in request"))
return
}
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))
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
failures := make(buserr.MultiErr)
for _, file := range files {
for _, file := range uploadFiles {
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"
if err := c.SaveUploadedFile(file, tmpFilename); err != nil {
_ = os.Remove(tmpFilename)
Expand All @@ -335,11 +360,11 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
global.LOG.Error(e)
continue
}
stat, statErr := os.Stat(dstFilename)
dstInfo, statErr := os.Stat(dstFilename)
if overwrite {
_ = os.Remove(dstFilename)
}

err = os.Rename(tmpFilename, dstFilename)
if err != nil {
_ = os.Remove(tmpFilename)
Expand All @@ -349,7 +374,9 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
continue
}
if statErr == nil {
_ = os.Chmod(dstFilename, stat.Mode())
_ = os.Chmod(dstFilename, dstInfo.Mode())
} else {
_ = os.Chmod(dstFilename, mode)
}
success++
}
Expand Down Expand Up @@ -499,37 +526,37 @@ func (b *BaseApi) DownloadChunkFiles(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrFileDownloadDir, err)
return
}

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-Length", strconv.FormatInt(info.Size(), 10))
c.Writer.Header().Set("Accept-Ranges", "bytes")

if c.Request.Header.Get("Range") != "" {
rangeHeader := c.Request.Header.Get("Range")
rangeArr := strings.Split(rangeHeader, "=")[1]
rangeParts := strings.Split(rangeArr, "-")

startPos, _ := strconv.ParseInt(rangeParts[0], 10, 64)

var endPos int64
if rangeParts[1] == "" {
endPos = info.Size() - 1
} else {
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.WriteHeader(http.StatusPartialContent)

buffer := make([]byte, 1024*1024)
file, err := os.Open(filePath)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
defer file.Close()

_, _ = file.Seek(startPos, 0)
reader := io.LimitReader(file, endPos-startPos+1)
_, err = io.CopyBuffer(c.Writer, reader, buffer)
Expand Down Expand Up @@ -567,17 +594,21 @@ func (b *BaseApi) Size(c *gin.Context) {
func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int) error {
op := files.NewFileOp()
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 = op.CreateDir(dstDir, os.ModePerm); err != nil {
if err = op.CreateDir(dstDir, mode); err != nil {
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 {
return err
}
defer targetFile.Close()


for i := 0; i < chunkCount; i++ {
chunkPath := filepath.Join(fileDir, fmt.Sprintf("%s.%d", fileName, i))
chunkData, err := os.ReadFile(chunkPath)
Expand All @@ -589,7 +620,7 @@ func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int)
return err
}
}

return files.NewFileOp().DeleteDir(fileDir)
}

Expand Down Expand Up @@ -639,7 +670,7 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
_ = os.MkdirAll(fileDir, 0755)
}
filePath := filepath.Join(fileDir, filename)

defer func() {
if err != nil {
_ = os.Remove(fileDir)
Expand All @@ -649,27 +680,27 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
emptyFile *os.File
chunkData []byte
)

emptyFile, err = os.Create(filePath)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
defer emptyFile.Close()

chunkData, err = io.ReadAll(uploadFile)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, buserr.WithMap(constant.ErrFileUpload, map[string]interface{}{"name": filename, "detail": err.Error()}, err))
return
}

chunkPath := filepath.Join(fileDir, fmt.Sprintf("%s.%d", filename, chunkIndex))
err = os.WriteFile(chunkPath, chunkData, 0644)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, buserr.WithMap(constant.ErrFileUpload, map[string]interface{}{"name": filename, "detail": err.Error()}, err))
return
}

if chunkIndex+1 == chunkCount {
err = mergeChunks(filename, fileDir, c.PostForm("path"), chunkCount)
if err != nil {
Expand Down
24 changes: 24 additions & 0 deletions backend/utils/files/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package files

import (
"bufio"
"fmt"
"github.com/spf13/afero"
"io"
"net/http"
Expand Down Expand Up @@ -117,3 +118,26 @@ func ReadFileByLine(filename string, page, pageSize int) ([]string, bool, error)

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
}
}

0 comments on commit 1e22769

Please sign in to comment.