Files
ragflow/internal/util/file.go
chanx bbb9b1df85 feat: Implement file upload and folder creation features by GO (#13903)
### What problem does this PR solve?

feat: Implement file upload and folder creation features

- Add file upload route in router.go
- Add file operation methods in dao/file.go
- Add util/file.go for file type detection and filename handling
- Implement file upload and folder creation endpoints in handler/file.go
- Implement file upload and folder creation logic in service/file.go
- Modify response message format in memory.go
- Add document count method in dao/document.go

### Type of change

- [x] New Feature (non-breaking change which adds functionality)
2026-04-02 20:21:04 +08:00

135 lines
3.3 KiB
Go

//
// Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package util
import (
"path/filepath"
"regexp"
"strings"
)
const (
FileTypePDF = "pdf"
FileTypeDOC = "doc"
FileTypeVISUAL = "visual"
FileTypeAURAL = "aural"
FileTypeFOLDER = "folder"
FileTypeOTHER = "other"
)
var (
filenameLenLimit = 255
)
func init() {
}
func normalizeFilename(filename string) (string, bool) {
if filename == "" {
return "", false
}
base := filepath.Base(filename)
base = strings.TrimSpace(base)
if base == "" || len(base) > filenameLenLimit {
return "", false
}
return strings.ToLower(base), true
}
func FilenameType(filename string) string {
normalized, ok := normalizeFilename(filename)
if !ok {
return FileTypeOTHER
}
if matched, _ := regexp.MatchString(`.*\.pdf$`, normalized); matched {
return FileTypePDF
}
docExtensions := []string{
"msg", "eml", "doc", "docx", "ppt", "pptx", "yml", "xml", "htm", "json", "jsonl", "ldjson",
"csv", "txt", "ini", "xls", "xlsx", "wps", "rtf", "hlp", "pages", "numbers", "key",
"md", "mdx", "py", "js", "java", "c", "cpp", "h", "php", "go", "ts", "sh", "cs", "kt",
"html", "sql", "epub",
}
for _, ext := range docExtensions {
if strings.HasSuffix(normalized, "."+ext) {
return FileTypeDOC
}
}
audioExtensions := []string{
"wav", "flac", "ape", "alac", "wv", "mp3", "aac", "ogg", "vorbis", "opus",
}
for _, ext := range audioExtensions {
if strings.HasSuffix(normalized, "."+ext) {
return FileTypeAURAL
}
}
visualExtensions := []string{
"jpg", "jpeg", "png", "tif", "gif", "pcx", "tga", "exif", "fpx", "svg", "psd", "cdr",
"pcd", "dxf", "ufo", "eps", "ai", "raw", "WMF", "webp", "avif", "apng", "icon", "ico",
"mpg", "mpeg", "avi", "rm", "rmvb", "mov", "wmv", "asf", "dat", "asx", "wvx", "mpe",
"mpa", "mp4", "mkv",
}
for _, ext := range visualExtensions {
if strings.HasSuffix(normalized, "."+ext) {
return FileTypeVISUAL
}
}
return FileTypeOTHER
}
func SanitizeFilename(filename string) string {
if filename == "" {
return ""
}
filename = strings.TrimSpace(filename)
if filename == "" {
return ""
}
filename = strings.ReplaceAll(filename, "\\", "/")
filename = strings.Trim(filename, "/")
parts := strings.Split(filename, "/")
var sanitizedParts []string
for _, part := range parts {
if part != "" && part != "." && part != ".." {
sanitizedParts = append(sanitizedParts, part)
}
}
unsafeRegex := regexp.MustCompile(`[^A-Za-z0-9_\-/]`)
for i, part := range sanitizedParts {
sanitizedParts[i] = unsafeRegex.ReplaceAllString(part, "")
}
result := strings.Join(sanitizedParts, "/")
return result
}
func GetFileExtension(filename string) string {
ext := filepath.Ext(filename)
if len(ext) > 0 && ext[0] == '.' {
return strings.ToLower(ext[1:])
}
return strings.ToLower(ext)
}