mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-08-05 15:20:30 +08:00
fix: can upload file larger than 10MB (#17801)
This commit is contained in:
@@ -37,6 +37,7 @@ const (
|
||||
EnvComponentExecTimeout = "COMPONENT_EXEC_TIMEOUT"
|
||||
EnvDocEngine = "DOC_ENGINE"
|
||||
EnvMaxFileNumPerUser = "MAX_FILE_NUM_PER_USER"
|
||||
EnvMaxContentLength = "MAX_CONTENT_LENGTH"
|
||||
EnvRAGFlowDictPath = "RAGFLOW_DICT_PATH"
|
||||
EnvDefaultSuperuserEmail = "DEFAULT_SUPERUSER_EMAIL"
|
||||
EnvDefaultSuperuserNickname = "DEFAULT_SUPERUSER_NICKNAME"
|
||||
|
||||
@@ -279,6 +279,12 @@ func (h *FileHandler) UploadFile(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
|
||||
if strings.Contains(contentType, "multipart/form-data") {
|
||||
uploadLimit := file.DeploymentUploadMaxBytes()
|
||||
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, uploadLimit)
|
||||
if cl := c.Request.ContentLength; cl > uploadLimit {
|
||||
common.ResponseWithCodeData(c, common.CodeBadRequest, nil, "request body exceeds deployment upload limit")
|
||||
return
|
||||
}
|
||||
if err := c.Request.ParseMultipartForm(32 << 20); err != nil {
|
||||
common.ResponseWithCodeData(c, common.CodeBadRequest, nil, "Failed to parse multipart form: "+err.Error())
|
||||
return
|
||||
@@ -313,7 +319,7 @@ func (h *FileHandler) UploadFile(c *gin.Context) {
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
result, err := h.fileService.UploadFile(ctx, userID, parentID, files)
|
||||
result, err := h.fileService.UploadFile(ctx, userID, parentID, files, uploadLimit)
|
||||
if err != nil {
|
||||
common.ErrorWithCode(c, common.CodeBadRequest, err.Error())
|
||||
return
|
||||
|
||||
@@ -45,6 +45,38 @@ func testFileService() *FileService {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeploymentUploadMaxBytes(t *testing.T) {
|
||||
t.Setenv("MAX_CONTENT_LENGTH", "")
|
||||
if got := DeploymentUploadMaxBytes(); got != defaultDeploymentUploadMaxBytes {
|
||||
t.Fatalf("default limit = %d, want %d", got, defaultDeploymentUploadMaxBytes)
|
||||
}
|
||||
|
||||
t.Setenv("MAX_CONTENT_LENGTH", "12345")
|
||||
if got := DeploymentUploadMaxBytes(); got != 12345 {
|
||||
t.Fatalf("configured limit = %d, want 12345", got)
|
||||
}
|
||||
|
||||
t.Setenv("MAX_CONTENT_LENGTH", "invalid")
|
||||
if got := DeploymentUploadMaxBytes(); got != defaultDeploymentUploadMaxBytes {
|
||||
t.Fatalf("invalid limit = %d, want default %d", got, defaultDeploymentUploadMaxBytes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadDeploymentUploadDataLimit(t *testing.T) {
|
||||
data, err := readDeploymentUploadData(strings.NewReader("hello"), 5)
|
||||
if err != nil {
|
||||
t.Fatalf("readDeploymentUploadData exact limit: %v", err)
|
||||
}
|
||||
if string(data) != "hello" {
|
||||
t.Fatalf("data = %q, want hello", string(data))
|
||||
}
|
||||
|
||||
_, err = readDeploymentUploadData(strings.NewReader("hello!"), 5)
|
||||
if err == nil || !strings.Contains(err.Error(), "deployment upload limit of 5 bytes") {
|
||||
t.Fatalf("error = %v, want deployment upload limit", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *fakeStorage) Type() string { return "fake_storage" }
|
||||
|
||||
func (f *fakeStorage) Health(ctx context.Context) bool {
|
||||
|
||||
@@ -11,12 +11,27 @@ import (
|
||||
"ragflow/internal/entity"
|
||||
"ragflow/internal/storage"
|
||||
"ragflow/internal/utility"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const defaultDeploymentUploadMaxBytes int64 = 1 << 30
|
||||
|
||||
func DeploymentUploadMaxBytes() int64 {
|
||||
raw := common.GetEnv(common.EnvMaxContentLength)
|
||||
if raw == "" {
|
||||
return defaultDeploymentUploadMaxBytes
|
||||
}
|
||||
n, err := strconv.ParseInt(raw, 10, 64)
|
||||
if err != nil || n <= 0 {
|
||||
return defaultDeploymentUploadMaxBytes
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// UploadFile uploads files to a folder
|
||||
func (s *FileService) UploadFile(ctx context.Context, tenantID, parentID string, files []*multipart.FileHeader) ([]map[string]interface{}, error) {
|
||||
func (s *FileService) UploadFile(ctx context.Context, tenantID, parentID string, files []*multipart.FileHeader, maxBytes int64) ([]map[string]interface{}, error) {
|
||||
if parentID == "" {
|
||||
rootFolder, err := s.fileDAO.GetRootFolder(ctx, dao.DB, tenantID)
|
||||
if err != nil {
|
||||
@@ -58,6 +73,10 @@ func (s *FileService) UploadFile(ctx context.Context, tenantID, parentID string,
|
||||
return nil, fmt.Errorf("no file selected")
|
||||
}
|
||||
|
||||
if maxBytes > 0 && fileHeader.Size > maxBytes {
|
||||
return nil, fmt.Errorf("file %s exceeds deployment upload limit of %d bytes", filename, maxBytes)
|
||||
}
|
||||
|
||||
fileType := utility.FilenameType(filename)
|
||||
|
||||
fileObjNames := s.parseFilePath(filename)
|
||||
@@ -101,7 +120,7 @@ func (s *FileService) UploadFile(ctx context.Context, tenantID, parentID string,
|
||||
}
|
||||
|
||||
var data []byte
|
||||
data, err = io.ReadAll(src)
|
||||
data, err = readDeploymentUploadData(src, maxBytes)
|
||||
src.Close()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read file data: %w", err)
|
||||
@@ -139,6 +158,21 @@ func (s *FileService) UploadFile(ctx context.Context, tenantID, parentID string,
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func readDeploymentUploadData(r io.Reader, maxBytes int64) ([]byte, error) {
|
||||
if maxBytes <= 0 {
|
||||
return io.ReadAll(r)
|
||||
}
|
||||
limited := &io.LimitedReader{R: r, N: maxBytes + 1}
|
||||
data, err := io.ReadAll(limited)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if int64(len(data)) > maxBytes {
|
||||
return nil, fmt.Errorf("file size exceeds deployment upload limit of %d bytes", maxBytes)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// UploadInfos mirrors Python's upload_info file branch: store raw bytes in the
|
||||
// per-user downloads bucket and return lightweight upload descriptors instead
|
||||
// of creating full File rows in the file-management tree.
|
||||
|
||||
Reference in New Issue
Block a user