diff --git a/backend/defConfig/errorCodes.json b/backend/defConfig/errorCodes.json index 79c05d2..319a7fc 100644 --- a/backend/defConfig/errorCodes.json +++ b/backend/defConfig/errorCodes.json @@ -13,7 +13,11 @@ "file_mime_err":-51, "file_size_err":-52, "file_name_err":-53, - "file_get_err":-54 + "file_get_err":-54, + "file_hash_err":-55, + "file_save_err":-56, + "file_not_found":-57, + "file_part_err":-58 } \ No newline at end of file diff --git a/backend/models/sql.go b/backend/models/sql.go index 4b3e41e..68ce82f 100644 --- a/backend/models/sql.go +++ b/backend/models/sql.go @@ -16,7 +16,7 @@ type TabFileInfo_ struct { ID uint `gorm:"primaryKey;autoIncrement"` Name string `gorm:"not null;size:256;index"` // 前端报告的文件名 Path string `gorm:"not null;size:300"` // - Sha256 string `gorm:"not null;size:256;index"` // + Sha256 string `gorm:"not null;size:64;index"` // Mime string `gorm:"size:64;index"` Type string `gorm:"size:64;index"` Const uint `gorm:"default:1;index"` diff --git a/backend/routers/api_Files.go b/backend/routers/api_Files.go index 9292bc9..bc51cf0 100644 --- a/backend/routers/api_Files.go +++ b/backend/routers/api_Files.go @@ -1,7 +1,11 @@ package routers import ( - "fmt" + "io" + "net/http" + "ops/models" + "path" + "path/filepath" "github.com/gin-gonic/gin" ) @@ -11,22 +15,151 @@ func file_save() { } func ApiFiles(r *gin.RouterGroup) { - r.POST("/upload", func(ctx *gin.Context) { - cookie := ctx.PostForm("cookie") - file, _ := ctx.FormFile("file") - //通过cookie获取用户信息 - _, err := AuthenticationAuthorityFromCookie(cookie) - if err == nil { + //getfile := r.Group("/get") //定义上传组 + r.GET("/:mode/:hash", func(ctx *gin.Context) { + hash := ctx.Param("hash") + mode := ctx.Param("mode") + // filename := ctx.Param("filename") + // fmt.Println(filename) + download := false + isPartOK := false + + if mode == "get" { + isPartOK = true + download = true + } + if mode == "download" { + isPartOK = true + download = false + } + if isPartOK { + file_info := models.TabFileInfo_{ + Sha256: hash, + } + if models.DB.Where(&file_info).First(&file_info).Error == nil { + ReturnFile(ctx, &file_info, download) + } else { + //fmt.Println("not fund") + ReturnJson(ctx, "file_not_found", nil) + } + } else { + ReturnJson(ctx, "file_part_err", nil) } - fmt.Println(file.Filename) - fmt.Println(cookie) - ReturnJson(ctx, "apiOK", nil) - }) - r.GET("/upload", func(ctx *gin.Context) { - ReturnJson(ctx, "apiOK", nil) }) + upload := r.Group("/upload") //定义上传组 + //上传文件的总接口,能上传什么文件应该由后端决定,前端仅做相应限制 + + upload.POST("/image", func(ctx *gin.Context) { + + cookie := ctx.PostForm("cookie") //首先需要判断用户是否登录 + + //通过cookie获取用户信息 + user, err := AuthenticationAuthorityFromCookie(cookie) + if err == nil { + file, err := ctx.FormFile("file") + + if err == nil { + if file.Filename != "" { + //限制文件大小 + if file.Size > 512 { + if file.Size < int64(models.ConfigsFile.MaxSize) { + + //判断文件mime是否合法 + // 打开文件流 + src_mime, _ := file.Open() + defer src_mime.Close() + // 读取前512字节用于MIME检测 + buffer := make([]byte, 512) + io.ReadFull(src_mime, buffer) + // 检测MIME类型 + mimeType := http.DetectContentType(buffer) + file_extname := models.ConfigsFile.AllowImageMime[mimeType] + if file_extname != "" { + filename := filepath.Base(file.Filename) // 防御性处理路径分隔符 + // 计算哈希值 + hash_str, err := models.SHA256HashFile(file) + if err == nil { + //fmt.Println(hash_str) + //fmt.Println(filename) + //这是上传的真实路径 + dst := path.Join(models.ConfigsFile.Pahts["image"], hash_str) + //fmt.Println(dst) + //判断文件是否存在避免重复保存 + if models.FileExists(dst) { + //fmt.Println("文件存在") + + } else { + //fmt.Println("文件no存在") + ferr := ctx.SaveUploadedFile(file, dst) + if ferr == nil { + //文件保存成功 + + } else { + + ReturnJson(ctx, "file_save_err", nil) + ctx.Abort() //end + return + } + } + //记录到数据库 + //先检查数据库有没有数据 + fund_file_info := models.TabFileInfo_{ + Name: filename, + Sha256: hash_str, + Mime: mimeType, + Type: "image", + UserID: user.ID, + } + fund_file_info2 := models.TabFileInfo_{} + + models.DB.Where(&fund_file_info).Find(&fund_file_info2) + + if fund_file_info2.ID != 0 { + fund_file_info2.Const += 1 + models.DB.Where(&fund_file_info).Updates(&fund_file_info2) + } else { + fund_file_info.Path = dst + models.DB.Create(&fund_file_info) // 传入指针 + } + + ReturnJson(ctx, "apiOK", nil) + + } else { + ReturnJson(ctx, "file_hash_err", nil) + } + + } else { + ReturnJson(ctx, "file_mime_err", nil) + } + } else { + ReturnJson(ctx, "file_size_err", nil) + } + + } else { + ReturnJson(ctx, "file_size_err", nil) + } + + } else { + ReturnJson(ctx, "file_name_err", nil) + } + + } else { + ReturnJson(ctx, "file_get_err", nil) + } + + } else { + ReturnJson(ctx, "userCookieError", nil) + } + + ReturnJson(ctx, "apiErr", nil) + }) + + // r.GET("/upload", func(ctx *gin.Context) { + // ReturnJson(ctx, "apiOK", nil) + // }) + } diff --git a/backend/routers/return.go b/backend/routers/return.go index efd890a..6429543 100644 --- a/backend/routers/return.go +++ b/backend/routers/return.go @@ -1,6 +1,10 @@ package routers -import "github.com/gin-gonic/gin" +import ( + "ops/models" + + "github.com/gin-gonic/gin" +) func ReturnJson(ctx *gin.Context, errMsg string, data map[string]interface{}) { var errCode = ErrorCode[errMsg] @@ -23,12 +27,12 @@ func ReturnJson(ctx *gin.Context, errMsg string, data map[string]interface{}) { } -func Return_file(ctx *gin.Context, file_path string, preview bool) { +func ReturnFile(ctx *gin.Context, file_info *models.TabFileInfo_, preview bool) { if preview { - ctx.File(file_path) + ctx.File(file_info.Path) } else { //需要从数据库拉取原始文件名 - //ctx.FileAttachment(file_info.Path, file_info.Name) + ctx.FileAttachment(file_info.Path, file_info.Name) } } diff --git a/frontent/ops_vue_js/src/components/useDropzone.vue b/frontent/ops_vue_js/src/components/useDropzone.vue index 9b7dc6c..ca40e5f 100644 --- a/frontent/ops_vue_js/src/components/useDropzone.vue +++ b/frontent/ops_vue_js/src/components/useDropzone.vue @@ -24,7 +24,11 @@ const prop= defineProps({ maxFilesize:{ type: Number, default: 10, - } + }, + uploadURL:{ + type: String, + default: "/api/files/upload", + }, }); // 初始化 Dropzone @@ -41,7 +45,7 @@ const initDropzone = () => { // 初始化新的实例 dropzoneInstance = new Dropzone(dropzoneElement.value, { - url: "/api/files/upload", // 上传地址 + url: prop.uploadURL, // 上传地址 // headers: { // user_cookie: "cccc", // }, diff --git a/frontent/ops_vue_js/src/views/purchase/addorder.vue b/frontent/ops_vue_js/src/views/purchase/addorder.vue index cc402c4..e8e673d 100644 --- a/frontent/ops_vue_js/src/views/purchase/addorder.vue +++ b/frontent/ops_vue_js/src/views/purchase/addorder.vue @@ -139,13 +139,14 @@ watch(locale, () => { >{{ t("purchase_addorder.remarks") }} 0/100 - + +