| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- package internal
- import (
- "bytes"
- "context"
- "filesdk"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "os"
- "path/filepath"
- "strings"
- "github.com/gogf/gf/v2/os/glog"
- "yx-dataset-server/app/schema"
- "yx-dataset-server/library/logger"
- "yx-dataset-server/library/minio"
- "yx-dataset-server/library/utils"
- )
- // NewFile 创建文件管理实例
- func NewFile() *File {
- return &File{}
- }
- // File 文件管理
- type File struct{}
- // Upload 上传文件
- func (a *File) Upload(ctx context.Context, r *http.Request, formKey, bucket string) (*schema.FileInfo, error) {
- file, header, err := r.FormFile(formKey)
- if err != nil {
- return nil, err
- }
- buff := new(bytes.Buffer)
- _, err = io.Copy(buff, file)
- if err != nil {
- return nil, err
- }
- headers := map[string]string{}
- headers["FILE-EXPIRE"] = utils.GetConfig("file_server.file-expire").String()
- result, err := filesdk.GetHandle().Upload(header.Filename, bucket, headers, buff.Bytes())
- if err != nil {
- glog.Debugf(ctx, "上传文件失败:%s", err.Error())
- return nil, err
- }
- err = filesdk.GetHandle().HandlePersistent(result.Hash)
- if err != nil {
- logger.Printf(ctx, "文件持久化设置失败:%s", err.Error())
- }
- info := &schema.FileInfo{
- URL: result.URL,
- Name: result.Name,
- Size: result.Size,
- MD5: result.Hash,
- }
- return info, nil
- }
- // Download 下载文件
- func (a *File) Download(ctx context.Context, fileUrl string) ([]byte, string, error) {
- Url, err := url.PathUnescape(fileUrl)
- if err != nil {
- return nil, "", err
- }
- stat, err := minio.GetClient().Stat(Url)
- if err != nil {
- fmt.Println("文件不存在")
- return nil, "", err
- }
- fileData, err := a.getSourceFileData(ctx, Url)
- if err != nil {
- return nil, "", err
- }
- return fileData, stat.ContentType, nil
- }
- func (a *File) getSourceFileData(ctx context.Context, file string) ([]byte, error) {
- var fileData []byte
- obj, err := minio.GetClient().Get(ctx, file)
- if err != nil {
- return nil, err
- }
- buf := new(bytes.Buffer)
- _, _ = io.Copy(buf, obj)
- _ = obj.Close()
- fileData = buf.Bytes()
- return fileData, nil
- }
- // 修正文件名,将半角 % 替换为全角 %(不替换的话文件将无法从浏览器中打开)
- func (a *File) getFileName(fileName string) string {
- return strings.ReplaceAll(fileName, "%", "%")
- }
- func (a *File) UploadFromTrainFolder(ctx context.Context, fileName, bucket string) (*schema.FileInfo, error) {
- // 1. 固定目标文件夹路径(和你的目录结构完全匹配)
- // 绝对路径兜底,避免程序运行目录错乱
- baseDir := "/opt/go/yx-dataset-server/file/产品知识"
- // 安全拼接完整路径,防止路径穿越漏洞
- fullFilePath := filepath.Join(baseDir, fileName)
- // 2. 打开本地文件(替代原 r.FormFile 表单取文件逻辑)
- localFile, err := os.Open(fullFilePath)
- if err != nil {
- return nil, fmt.Errorf("读取产品知识文件失败: %w", err)
- }
- // 函数结束自动关闭文件,防止句柄泄漏
- defer localFile.Close()
- // 3. 读取文件到缓冲区(完全保留你原有的内存拷贝逻辑)
- buff := new(bytes.Buffer)
- _, err = io.Copy(buff, localFile)
- if err != nil {
- return nil, fmt.Errorf("文件读取缓存失败: %w", err)
- }
- // 4. 原有SDK上传逻辑1:1保留
- headers := map[string]string{}
- headers["FILE-EXPIRE"] = utils.GetConfig("file_server.file-expire").String()
- result, err := filesdk.GetHandle().Upload(fileName, bucket, headers, buff.Bytes())
- if err != nil {
- glog.Debugf(ctx, "上传文件失败:%s", err.Error())
- return nil, err
- }
- // 5. 文件持久化逻辑完整保留
- err = filesdk.GetHandle().HandlePersistent(result.Hash)
- if err != nil {
- logger.Printf(ctx, "文件持久化设置失败:%s", err.Error())
- }
- // 6. 返回结构完全兼容原定义
- info := &schema.FileInfo{
- URL: result.URL,
- Name: result.Name,
- Size: result.Size,
- MD5: result.Hash,
- }
- return info, nil
- }
- func (a *File) UploadAllTrainFiles(ctx context.Context, path, bucket string) ([]*schema.FileInfo, error) {
- baseDir := fmt.Sprintf("/opt/go/yx-dataset-server/file/%s", path)
- fmt.Printf(fmt.Sprintf("正在读取【%s】目录下的所有文件...\n", path))
- // 读取目录内所有文件
- dirEntries, err := os.ReadDir(baseDir)
- if err != nil {
- return nil, fmt.Errorf("读取文件失败: %w", err)
- }
- var uploadResult []*schema.FileInfo
- fmt.Printf("开始上传文件...\n")
- for k, entry := range dirEntries {
- if entry.IsDir() {
- continue
- }
- fmt.Printf("正在处理第%d个文件:%s\n", k, entry.Name())
- // 调用上面的单文件上传方法
- fileInfo, err := a.UploadFromTrainFolder(ctx, entry.Name(), bucket)
- if err != nil {
- glog.Debugf(ctx, "文件【%s】上传跳过: %s", entry.Name(), err.Error())
- continue
- }
- uploadResult = append(uploadResult, fileInfo)
- }
- return uploadResult, nil
- }
- // UploadFromLocal 从本地文件路径上传文件
- func (a *File) UploadFromLocal(ctx context.Context, filePath string, bucket string) (*schema.FileInfo, error) {
- logger.Printf(ctx, "[文件上传] 开始上传本地文件: %s, bucket: %s", filePath, bucket)
- file, err := os.Open(filePath)
- if err != nil {
- logger.Printf(ctx, "[文件上传] 打开文件失败: %s, 错误: %s", filePath, err.Error())
- return nil, fmt.Errorf("打开文件失败: %s", err.Error())
- }
- defer file.Close()
- // 获取文件信息
- fileInfo, err := file.Stat()
- if err != nil {
- logger.Printf(ctx, "[文件上传] 获取文件信息失败: %s, 错误: %s", filePath, err.Error())
- return nil, fmt.Errorf("获取文件信息失败: %s", err.Error())
- }
- fileSize := fileInfo.Size()
- fileName := fileInfo.Name()
- logger.Printf(ctx, "[文件上传] 文件信息 - 名称: %s, 大小: %d bytes", fileName, fileSize)
- // 读取文件内容
- data, err := io.ReadAll(file)
- if err != nil {
- logger.Printf(ctx, "[文件上传] 读取文件内容失败: %s, 错误: %s", filePath, err.Error())
- return nil, fmt.Errorf("读取文件内容失败: %s", err.Error())
- }
- logger.Printf(ctx, "[文件上传] 文件读取完成: %s, 实际读取: %d bytes", filePath, len(data))
- headers := map[string]string{}
- headers["FILE-EXPIRE"] = utils.GetConfig("file_server.file-expire").String()
- logger.Printf(ctx, "[文件上传] 开始上传到Minio: %s", filePath)
- result, err := filesdk.GetHandle().Upload(fileName, bucket, headers, data)
- if err != nil {
- logger.Printf(ctx, "[文件上传] Minio上传失败: %s, 错误: %s", filePath, err.Error())
- glog.Debugf(ctx, "上传文件失败: %s", err.Error())
- return nil, err
- }
- logger.Printf(ctx, "[文件上传] Minio上传成功: %s -> %s, MD5: %s", filePath, result.URL, result.Hash)
- err = filesdk.GetHandle().HandlePersistent(result.Hash)
- if err != nil {
- logger.Printf(ctx, "[文件上传] 文件持久化设置失败: %s, 错误: %s", filePath, err.Error())
- }
- info := &schema.FileInfo{
- URL: result.URL,
- Name: result.Name,
- Size: result.Size,
- MD5: result.Hash,
- }
- logger.Printf(ctx, "[文件上传] 上传完成: %s", filePath)
- return info, nil
- }
|