diff --git a/server/api/v1/exa_simple_uploader.go b/server/api/v1/exa_simple_uploader.go new file mode 100644 index 00000000..5067299e --- /dev/null +++ b/server/api/v1/exa_simple_uploader.go @@ -0,0 +1,87 @@ +package v1 + +import ( + "fmt" + "gin-vue-admin/global/response" + "gin-vue-admin/model" + "gin-vue-admin/service" + "gin-vue-admin/utils" + "github.com/gin-gonic/gin" +) + +// @Tags SimpleUploader +// @Summary 断点续传插件版示例 +// @Security ApiKeyAuth +// @accept multipart/form-data +// @Produce application/json +// @Param file formData file true "断点续传插件版示例" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"上传成功"}" +// @Router /simpleUploader/upload [post] +func SimpleUploaderUpload(c *gin.Context) { + var chunk model.ExaSimpleUploader + _, header, err := c.Request.FormFile("file") + chunk.Filename = c.PostForm("filename") + chunk.ChunkNumber = c.PostForm("chunkNumber") + chunk.CurrentChunkSize = c.PostForm("currentChunkSize") + chunk.Identifier = c.PostForm("identifier") + chunk.TotalSize = c.PostForm("totalSize") + chunk.TotalChunks = c.PostForm("totalChunks") + var chunkDir = "./chunk/" + chunk.Identifier + "/" + hasDir, _ := utils.PathExists(chunkDir) + if !hasDir { + utils.CreateDir(chunkDir) + } + chunkPath := chunkDir + chunk.Filename + chunk.ChunkNumber + err = c.SaveUploadedFile(header, chunkPath) + if err != nil { + response.FailWithMessage(fmt.Sprintf("切片创建失败,%v", err), c) + return + } + chunk.CurrentChunkPath = chunkPath + err = service.SaveChunk(chunk) + if err != nil { + response.FailWithMessage(fmt.Sprintf("切片创建失败,%v", err), c) + return + } else { + response.Ok(c) + } +} + +// @Tags SimpleUploader +// @Summary 断点续传插件版示例 +// @Security ApiKeyAuth + +// @Produce application/json +// @Param params md5 get "测试文件是否已经存在和判断已经上传过的切片" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}" +// @Router /simpleUploader/checkFileMd5 [get] +func CheckFileMd5(c *gin.Context) { + md5 := c.Query("md5") + err, chunks, isDone := service.CheckFileMd5(md5) + if err != nil { + response.FailWithMessage(fmt.Sprintf("md5读取失败,%v", err), c) + } else { + response.OkWithData(gin.H{ + "chunks": chunks, + "isDone": isDone, + }, c) + } +} + +// @Tags SimpleUploader +// @Summary 合并文件 +// @Security ApiKeyAuth +// @Produce application/json +// @Param params md5 get "合并文件" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"合并成功"}" +// @Router /simpleUploader/mergeFileMd5 [get] +func MergeFileMd5(c *gin.Context) { + md5 := c.Query("md5") + fileName := c.Query("fileName") + err := service.MergeFileMd5(md5, fileName) + if err != nil { + response.FailWithMessage(fmt.Sprintf("md5读取失败,%v", err), c) + } else { + response.OkWithData(gin.H{}, c) + } +} diff --git a/server/db/20200815_update_patch.sql b/server/db/20200815_update_patch.sql new file mode 100644 index 00000000..a7bdf5cd --- /dev/null +++ b/server/db/20200815_update_patch.sql @@ -0,0 +1,27 @@ +INSERT INTO `casbin_rule` VALUES ('p', '888', '/simpleUploader/upload', 'POST', '', '', ''); +INSERT INTO `casbin_rule` VALUES ('p', '888', '/simpleUploader/checkFileMd5', 'GET', '', '', ''); +INSERT INTO `casbin_rule` VALUES ('p', '888', '/simpleUploader/mergeFileMd5', 'GET', '', '', ''); + +-- ---------------------------- +-- Table structure for exa_simple_uploaders +-- ---------------------------- +DROP TABLE IF EXISTS `exa_simple_uploaders`; +CREATE TABLE `exa_simple_uploaders` ( + `chunk_number` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '当前切片标记', + `current_chunk_size` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '当前切片容量', + `current_chunk_path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '切片本地路径', + `total_size` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '总容量', + `identifier` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '文件标识(md5)', + `filename` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '文件名', + `total_chunks` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '切片总数', + `is_done` tinyint(1) NULL DEFAULT NULL COMMENT '是否上传完成', + `file_path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '文件本地路径' +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Compact; + +INSERT INTO `sys_apis` VALUES (100, '2020-08-15 12:10:55', '2020-08-15 12:10:55', NULL, NULL, '/simpleUploader/upload', '插件版分片上传', 'simpleUploader', 'POST'); +INSERT INTO `sys_apis` VALUES (101, '2020-08-15 19:53:53', '2020-08-15 19:53:53', NULL, NULL, '/simpleUploader/checkFileMd5', '文件完整度验证', 'simpleUploader', 'GET'); +INSERT INTO `sys_apis` VALUES (102, '2020-08-15 22:28:04', '2020-08-15 22:28:04', NULL, NULL, '/simpleUploader/mergeFileMd5', '上传完成合并文件', 'simpleUploader', 'GET'); + +INSERT INTO `sys_authority_menus` VALUES ('888', 53); + +INSERT INTO `sys_base_menus` VALUES (53, '2020-08-15 11:41:49', '2020-08-15 11:43:15', NULL, 0, 19, 'simpleUploader', 'simpleUploader', 0, 'view/example/simpleUploader/simpleUploader', '断点续传(插件版)', 'upload', NULL, 6, 0, 0); diff --git a/server/db/qmplus.sql b/server/db/qmplus.sql index a8152f17..22ce817d 100644 --- a/server/db/qmplus.sql +++ b/server/db/qmplus.sql @@ -11,7 +11,7 @@ Target Server Version : 50640 File Encoding : 65001 - Date: 11/08/2020 23:22:30 + Date: 15/08/2020 23:09:13 */ SET NAMES utf8mb4; @@ -176,6 +176,9 @@ INSERT INTO `casbin_rule` VALUES ('p', '888', '/sysOperationRecord/updateSysOper INSERT INTO `casbin_rule` VALUES ('p', '888', '/sysOperationRecord/findSysOperationRecord', 'GET', '', '', ''); INSERT INTO `casbin_rule` VALUES ('p', '888', '/sysOperationRecord/getSysOperationRecordList', 'GET', '', '', ''); INSERT INTO `casbin_rule` VALUES ('p', '888', '/sysOperationRecord/deleteSysOperationRecordByIds', 'DELETE', '', '', ''); +INSERT INTO `casbin_rule` VALUES ('p', '888', '/simpleUploader/upload', 'POST', '', '', ''); +INSERT INTO `casbin_rule` VALUES ('p', '888', '/simpleUploader/checkFileMd5', 'GET', '', '', ''); +INSERT INTO `casbin_rule` VALUES ('p', '888', '/simpleUploader/mergeFileMd5', 'GET', '', '', ''); -- ---------------------------- -- Table structure for exa_customers @@ -257,6 +260,22 @@ CREATE TABLE `exa_files` ( INDEX `idx_exa_files_deleted_at`(`deleted_at`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; +-- ---------------------------- +-- Table structure for exa_simple_uploaders +-- ---------------------------- +DROP TABLE IF EXISTS `exa_simple_uploaders`; +CREATE TABLE `exa_simple_uploaders` ( + `chunk_number` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '当前切片标记', + `current_chunk_size` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '当前切片容量', + `current_chunk_path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '切片本地路径', + `total_size` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '总容量', + `identifier` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '文件标识(md5)', + `filename` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '文件名', + `total_chunks` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '切片总数', + `is_done` tinyint(1) NULL DEFAULT NULL COMMENT '是否上传完成', + `file_path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '文件本地路径' +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Compact; + -- ---------------------------- -- Table structure for jwt_blacklists -- ---------------------------- @@ -269,7 +288,7 @@ CREATE TABLE `jwt_blacklists` ( `jwt` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT 'jwt', PRIMARY KEY (`id`) USING BTREE, INDEX `idx_jwt_blacklists_deleted_at`(`deleted_at`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 57 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; -- ---------------------------- -- Table structure for sys_apis @@ -288,7 +307,7 @@ CREATE TABLE `sys_apis` ( PRIMARY KEY (`id`) USING BTREE, INDEX `idx_apis_deleted_at`(`deleted_at`) USING BTREE, INDEX `idx_sys_apis_deleted_at`(`deleted_at`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 106 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; +) ENGINE = InnoDB AUTO_INCREMENT = 103 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; -- ---------------------------- -- Records of sys_apis @@ -356,6 +375,9 @@ INSERT INTO `sys_apis` VALUES (96, '2020-07-05 14:34:20', '2020-07-05 14:34:20', INSERT INTO `sys_apis` VALUES (97, '2020-07-05 15:02:07', '2020-07-05 15:02:07', NULL, NULL, '/autoCode/getDB', '获取所有数据库', 'autoCode', 'GET'); INSERT INTO `sys_apis` VALUES (98, '2020-07-05 16:32:08', '2020-07-05 16:32:08', NULL, NULL, '/autoCode/getColume', '获取所选table的所有字段', 'autoCode', 'GET'); INSERT INTO `sys_apis` VALUES (99, '2020-07-07 15:59:53', '2020-07-07 15:59:53', NULL, NULL, '/sysOperationRecord/deleteSysOperationRecordByIds', '批量删除操作历史', 'sysOperationRecord', 'DELETE'); +INSERT INTO `sys_apis` VALUES (100, '2020-08-15 12:10:55', '2020-08-15 12:10:55', NULL, NULL, '/simpleUploader/upload', '插件版分片上传', 'simpleUploader', 'POST'); +INSERT INTO `sys_apis` VALUES (101, '2020-08-15 19:53:53', '2020-08-15 19:53:53', NULL, NULL, '/simpleUploader/checkFileMd5', '文件完整度验证', 'simpleUploader', 'GET'); +INSERT INTO `sys_apis` VALUES (102, '2020-08-15 22:28:04', '2020-08-15 22:28:04', NULL, NULL, '/simpleUploader/mergeFileMd5', '上传完成合并文件', 'simpleUploader', 'GET'); -- ---------------------------- -- Table structure for sys_authorities @@ -417,6 +439,7 @@ INSERT INTO `sys_authority_menus` VALUES ('888', 42); INSERT INTO `sys_authority_menus` VALUES ('888', 50); INSERT INTO `sys_authority_menus` VALUES ('888', 51); INSERT INTO `sys_authority_menus` VALUES ('888', 52); +INSERT INTO `sys_authority_menus` VALUES ('888', 53); INSERT INTO `sys_authority_menus` VALUES ('8881', 1); INSERT INTO `sys_authority_menus` VALUES ('8881', 2); INSERT INTO `sys_authority_menus` VALUES ('8881', 18); @@ -460,7 +483,7 @@ CREATE TABLE `sys_base_menu_parameters` ( `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE, INDEX `idx_sys_base_menu_parameters_deleted_at`(`deleted_at`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Compact; +) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Compact; -- ---------------------------- -- Table structure for sys_base_menus @@ -492,7 +515,7 @@ CREATE TABLE `sys_base_menus` ( -- Records of sys_base_menus -- ---------------------------- INSERT INTO `sys_base_menus` VALUES (1, '2019-09-19 22:05:18', '2020-05-30 15:43:06', NULL, 0, 0, 'dashboard', 'dashboard', 0, 'view/dashboard/index.vue', '仪表盘', 'setting', '仪表盘', 1, 0, 0); -INSERT INTO `sys_base_menus` VALUES (2, '2019-09-19 22:06:17', '2020-05-10 21:31:50', NULL, 0, 0, 'about', 'about', 0, 'view/about/index.vue', '关于我们', 'info', '测试菜单', 7, 0, 0); +INSERT INTO `sys_base_menus` VALUES (2, '2019-09-19 22:06:17', '2020-08-11 23:26:21', NULL, 0, 0, 'about', 'about', 0, 'view/about/index.vue', '关于我们', 'info', '测试菜单', 7, 0, 0); INSERT INTO `sys_base_menus` VALUES (3, '2019-09-19 22:06:38', '2020-04-24 10:16:43', NULL, 0, 0, 'admin', 'superAdmin', 0, 'view/superAdmin/index.vue', '超级管理员', 'user-solid', '超级管理员', 3, 0, 0); INSERT INTO `sys_base_menus` VALUES (4, '2019-09-19 22:11:53', '2020-05-30 15:43:25', NULL, 0, 3, 'authority', 'authority', 0, 'view/superAdmin/authority/authority.vue', '角色管理', 's-custom', '角色管理', 1, 0, 0); INSERT INTO `sys_base_menus` VALUES (5, '2019-09-19 22:13:18', '2020-04-30 17:45:27', NULL, 0, 3, 'menu', 'menu', 0, 'view/superAdmin/menu/menu.vue', '菜单管理', 's-order', '菜单管理', 2, 1, 0); @@ -515,6 +538,7 @@ INSERT INTO `sys_base_menus` VALUES (45, '2020-04-29 17:19:34', '2020-07-04 18:2 INSERT INTO `sys_base_menus` VALUES (50, '2020-06-24 19:49:54', '2020-06-28 20:34:47', NULL, 0, 3, 'dictionary', 'dictionary', 0, 'view/superAdmin/dictionary/sysDictionary.vue', '字典管理', 'notebook-2', NULL, 5, 0, 0); INSERT INTO `sys_base_menus` VALUES (51, '2020-06-24 19:51:33', '2020-06-28 20:35:04', NULL, 0, 3, 'dictionaryDetail/:id', 'dictionaryDetail', 1, 'view/superAdmin/dictionary/sysDictionaryDetail.vue', '字典详情', 's-order', NULL, 1, 0, 0); INSERT INTO `sys_base_menus` VALUES (52, '2020-06-29 13:31:17', '2020-07-07 16:05:34', NULL, 0, 3, 'operation', 'operation', 0, 'view/superAdmin/operation/sysOperationRecord.vue', '操作历史', 'time', NULL, 6, 0, 0); +INSERT INTO `sys_base_menus` VALUES (53, '2020-08-15 11:41:49', '2020-08-15 11:43:15', NULL, 0, 19, 'simpleUploader', 'simpleUploader', 0, 'view/example/simpleUploader/simpleUploader', '断点续传(插件版)', 'upload', NULL, 6, 0, 0); -- ---------------------------- -- Table structure for sys_data_authority_id @@ -636,12 +660,7 @@ CREATE TABLE `sys_operation_records` ( `resp` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL COMMENT '响应Body', PRIMARY KEY (`id`) USING BTREE, INDEX `idx_sys_operation_records_deleted_at`(`deleted_at`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 343 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Compact; - --- ---------------------------- --- Records of sys_operation_records --- ---------------------------- -INSERT INTO `sys_operation_records` VALUES (342, '2020-08-11 23:21:30', '2020-08-11 23:21:30', NULL, '127.0.0.1', 'POST', '/authority/getAuthorityList', 200, 3906600, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36', '', '{\"page\":1,\"pageSize\":999}', 10, '{\"code\":0,\"data\":{\"list\":[{\"CreatedAt\":\"2020-04-04T11:44:56+08:00\",\"UpdatedAt\":\"2020-06-13T16:07:37+08:00\",\"DeletedAt\":null,\"authorityId\":\"888\",\"authorityName\":\"普通用户\",\"parentId\":\"0\",\"dataAuthorityId\":[{\"CreatedAt\":\"2020-04-04T11:44:56+08:00\",\"UpdatedAt\":\"2020-06-13T16:07:37+08:00\",\"DeletedAt\":null,\"authorityId\":\"888\",\"authorityName\":\"普通用户\",\"parentId\":\"0\",\"dataAuthorityId\":null,\"children\":null,\"menus\":null},{\"CreatedAt\":\"2020-04-04T11:44:56+08:00\",\"UpdatedAt\":\"2020-04-24T10:16:42+08:00\",\"DeletedAt\":null,\"authorityId\":\"8881\",\"authorityName\":\"普通用户子角色\",\"parentId\":\"888\",\"dataAuthorityId\":null,\"children\":null,\"menus\":null},{\"CreatedAt\":\"2020-04-04T11:44:56+08:00\",\"UpdatedAt\":\"2020-04-24T10:16:42+08:00\",\"DeletedAt\":null,\"authorityId\":\"9528\",\"authorityName\":\"测试角色\",\"parentId\":\"0\",\"dataAuthorityId\":null,\"children\":null,\"menus\":null}],\"children\":[{\"CreatedAt\":\"2020-04-04T11:44:56+08:00\",\"UpdatedAt\":\"2020-04-24T10:16:42+08:00\",\"DeletedAt\":null,\"authorityId\":\"8881\",\"authorityName\":\"普通用户子角色\",\"parentId\":\"888\",\"dataAuthorityId\":[],\"children\":[],\"menus\":null}],\"menus\":null},{\"CreatedAt\":\"2020-04-04T11:44:56+08:00\",\"UpdatedAt\":\"2020-04-24T10:16:42+08:00\",\"DeletedAt\":null,\"authorityId\":\"9528\",\"authorityName\":\"测试角色\",\"parentId\":\"0\",\"dataAuthorityId\":[{\"CreatedAt\":\"2020-04-04T11:44:56+08:00\",\"UpdatedAt\":\"2020-04-24T10:16:42+08:00\",\"DeletedAt\":null,\"authorityId\":\"8881\",\"authorityName\":\"普通用户子角色\",\"parentId\":\"888\",\"dataAuthorityId\":null,\"children\":null,\"menus\":null},{\"CreatedAt\":\"2020-04-04T11:44:56+08:00\",\"UpdatedAt\":\"2020-04-24T10:16:42+08:00\",\"DeletedAt\":null,\"authorityId\":\"9528\",\"authorityName\":\"测试角色\",\"parentId\":\"0\",\"dataAuthorityId\":null,\"children\":null,\"menus\":null}],\"children\":[],\"menus\":null}],\"total\":0,\"page\":1,\"pageSize\":999},\"msg\":\"操作成功\"}'); +) ENGINE = InnoDB AUTO_INCREMENT = 358 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Compact; -- ---------------------------- -- Table structure for sys_users diff --git a/server/initialize/db_table.go b/server/initialize/db_table.go index bf44f203..6ea6bb1b 100644 --- a/server/initialize/db_table.go +++ b/server/initialize/db_table.go @@ -21,6 +21,7 @@ func DBTables() { model.ExaFileUploadAndDownload{}, model.ExaFile{}, model.ExaFileChunk{}, + model.ExaSimpleUploader{}, model.ExaCustomer{}, model.SysOperationRecord{}, ) diff --git a/server/initialize/router.go b/server/initialize/router.go index 5ba0a376..aac158ec 100644 --- a/server/initialize/router.go +++ b/server/initialize/router.go @@ -29,6 +29,7 @@ func Routers() *gin.Engine { router.InitAuthorityRouter(ApiGroup) // 注册角色路由 router.InitApiRouter(ApiGroup) // 注册功能api路由 router.InitFileUploadAndDownloadRouter(ApiGroup) // 文件上传下载功能路由 + router.InitSimpleUploaderRouter(ApiGroup) // 断点续传(插件版) router.InitWorkflowRouter(ApiGroup) // 工作流相关路由 router.InitCasbinRouter(ApiGroup) // 权限相关路由 router.InitJwtRouter(ApiGroup) // jwt相关路由 @@ -38,6 +39,7 @@ func Routers() *gin.Engine { router.InitSysDictionaryDetailRouter(ApiGroup) // 字典详情管理 router.InitSysDictionaryRouter(ApiGroup) // 字典管理 router.InitSysOperationRecordRouter(ApiGroup) // 操作记录 + global.GVA_LOG.Info("router register success") return Router } diff --git a/server/model/exa_simple_uploader.go b/server/model/exa_simple_uploader.go new file mode 100644 index 00000000..2f7a2a17 --- /dev/null +++ b/server/model/exa_simple_uploader.go @@ -0,0 +1,13 @@ +package model + +type ExaSimpleUploader struct { + ChunkNumber string `json:"chunkNumber" gorm:"comment:'当前切片标记'"` + CurrentChunkSize string `json:"currentChunkSize" gorm:"comment:'当前切片容量'"` + CurrentChunkPath string `json:"currentChunkPath" gorm:"comment:'切片本地路径'"` + TotalSize string `json:"totalSize" gorm:"comment:'总容量'"` + Identifier string `json:"identifier" gorm:"comment:'文件标识(md5)'"` + Filename string `json:"filename" gorm:"comment:'文件名'"` + TotalChunks string `json:"totalChunks" gorm:"comment:'切片总数'"` + IsDone bool `json:"isDone" gorm:"comment:'是否上传完成'"` + FilePath string `json:"filePath" gorm:"comment:'文件本地路径'"` +} diff --git a/server/router/exa_simple_uploader.go b/server/router/exa_simple_uploader.go new file mode 100644 index 00000000..7197d638 --- /dev/null +++ b/server/router/exa_simple_uploader.go @@ -0,0 +1,18 @@ +package router + +import ( + "gin-vue-admin/api/v1" + "gin-vue-admin/middleware" + "github.com/gin-gonic/gin" +) + +func InitSimpleUploaderRouter(Router *gin.RouterGroup) { + ApiRouter := Router.Group("simpleUploader"). + Use(middleware.JWTAuth()). + Use(middleware.CasbinHandler()) + { + ApiRouter.POST("upload", v1.SimpleUploaderUpload) // 上传功能 + ApiRouter.GET("checkFileMd5", v1.CheckFileMd5) // 文件完整度验证 + ApiRouter.GET("mergeFileMd5", v1.MergeFileMd5) // 合并文件 + } +} diff --git a/server/service/exa_simple_uploader.go b/server/service/exa_simple_uploader.go new file mode 100644 index 00000000..06f067cf --- /dev/null +++ b/server/service/exa_simple_uploader.go @@ -0,0 +1,72 @@ +package service + +import ( + "fmt" + "gin-vue-admin/global" + "gin-vue-admin/model" + "io/ioutil" + "os" + "strconv" +) + +// 保存文件切片路径 +func SaveChunk(uploader model.ExaSimpleUploader) (err error) { + return global.GVA_DB.Create(uploader).Error +} + +// 检查文件是否已经上传过 +func CheckFileMd5(md5 string) (err error, uploads []model.ExaSimpleUploader, isDone bool) { + err = global.GVA_DB.Find(&uploads, "identifier = ? AND is_done = ?", md5, false).Error + isDone = global.GVA_DB.First(&model.ExaSimpleUploader{}, "identifier = ? AND is_done = ?", md5, true).RecordNotFound() + return err, uploads, !isDone +} + +// 合并文件 +func MergeFileMd5(md5 string, fileName string) (err error) { + finishDir := "./finish/" + dir := "./chunk/" + md5 + //如果文件上传成功 不做后续操作 通知成功即可 + notFinish := global.GVA_DB.First(&model.ExaSimpleUploader{}, "identifier = ? AND is_done = ?", md5, true).RecordNotFound() + if !notFinish { + return nil + } + + //打开切片文件夹 + rd, err := ioutil.ReadDir(dir) + _ = os.MkdirAll(finishDir, os.ModePerm) + //创建目标文件 + fd, _ := os.OpenFile(finishDir+fileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) + //将切片文件按照顺序写入 + for k := range rd { + content, _ := ioutil.ReadFile(dir + "/" + fileName + strconv.Itoa(k+1)) + _, err = fd.Write(content) + if err != nil { + _ = os.Remove(finishDir + fileName) + } + } + //关闭文件 + defer fd.Close() + + if err != nil { + return err + } + //创建事务 + tx := global.GVA_DB.Begin() + //删除切片信息 + err = tx.Delete(&model.ExaSimpleUploader{}, "identifier = ? AND is_done = ?", md5, false).Error + // 添加文件信息 + err = tx.Create(&model.ExaSimpleUploader{ + Identifier: md5, + IsDone: true, + FilePath: finishDir + fileName, + Filename: fileName, + }).Error + if err != nil { + fmt.Println(err) + tx.Rollback() + } + tx.Commit() + //清除切片 + err = os.RemoveAll(dir) + return +} diff --git a/web/package.json b/web/package.json index e7a2c8d3..3930b412 100644 --- a/web/package.json +++ b/web/package.json @@ -27,6 +27,7 @@ "vue-particle-line": "^0.1.4", "vue-quill-editor": "^3.0.6", "vue-router": "^3.1.3", + "vue-simple-uploader": "^0.7.4", "vuescroll": "^4.14.4", "vuex": "^3.1.1", "vuex-persist": "^2.1.0" diff --git a/web/src/api/simpleUploader.js b/web/src/api/simpleUploader.js new file mode 100644 index 00000000..7cfe7d6c --- /dev/null +++ b/web/src/api/simpleUploader.js @@ -0,0 +1,36 @@ + + +import service from '@/utils/request' + +// @Tags SimpleUploader +// @Summary 断点续传插件版示例 +// @Security ApiKeyAuth + +// @Produce application/json +// @Param params md5 get "测试文件是否已经存在和判断已经上传过的切片" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}" +// @Router /simpleUploader/checkFileMd5 [get] +export const checkFileMd5 = (params) => { + return service({ + url: "/simpleUploader/checkFileMd5", + method: 'get', + params + }) +} + + +// @Tags SimpleUploader +// @Summary 合并文件 +// @Security ApiKeyAuth +// @Produce application/json +// @Param params md5 get "合并文件" +// @Success 200 {string} string "{"success":true,"data":{},"msg":"合并成功"}" +// @Router /simpleUploader/mergeFileMd5 [get] +export const mergeFileMd5 = (params) => { + return service({ + url: "/simpleUploader/mergeFileMd5", + method: 'get', + params + }) +} + diff --git a/web/src/main.js b/web/src/main.js index 4f1dea42..d9c33b43 100644 --- a/web/src/main.js +++ b/web/src/main.js @@ -51,6 +51,8 @@ import {auth} from '@/directive/auth' // 按钮权限指令 auth(Vue) +import uploader from 'vue-simple-uploader' +Vue.use(uploader) new Vue({ render: h => h(App), diff --git a/web/src/view/example/simpleUploader/simpleUploader.vue b/web/src/view/example/simpleUploader/simpleUploader.vue new file mode 100644 index 00000000..c9b40d0b --- /dev/null +++ b/web/src/view/example/simpleUploader/simpleUploader.vue @@ -0,0 +1,163 @@ + + + + + \ No newline at end of file