diff --git a/bookshelfplus-frontend/views/dashboard/admin/FileManage_Upload.html b/bookshelfplus-frontend/views/dashboard/admin/FileManage_Upload.html index 04d5219..2bcf2a8 100644 --- a/bookshelfplus-frontend/views/dashboard/admin/FileManage_Upload.html +++ b/bookshelfplus-frontend/views/dashboard/admin/FileManage_Upload.html @@ -374,9 +374,35 @@ if (status === "success") { console.log("data", data); // 取得预授权URL,使用该URL进行文件上传 - uploadFile(file, data.url, data.fileId); + uploadFile(file, data.url, data.fileId, data.fileObjectId); } else { - alert(`出错啦!${data.errMsg} (错误码: ${data.errCode}) `); + if (data.errCode == "60001") { + // 文件已存在 + console.log(`出错啦!${data.errMsg} (错误码: ${data.errCode}) `); + + // 再次发送请求,查询这个已存在文件的 fileId + postRequest("/file/getFileByHash", { token: localStorageUtils.getToken(), fileSha1: fileInfo.fileSha1 }) + .then(function (responseData) { + var axiosData = responseData.data; + var status = axiosData.status; + var data = axiosData.data; + if (status === "success") { + console.log(data); + + // 查询到之后,询问用户是否跳转到文件详情页 + var isRedirect = confirm(`文件已存在,是否前往查看详情?\n(文件ID: ${data.id})`); + if (isRedirect) + location.href = `<%= pageUrl %>../detail?fileId=${data.id}`; + } else { + alert(`出错啦!${data.errMsg} (错误码: ${data.errCode}) `); + } + }).catch(function (error) { + console.log(error); + alert("无法连接到服务器,请检查网络连接!"); + }); + } else { + alert(`出错啦!${data.errMsg} (错误码: ${data.errCode}) `); + } } }).catch(function (error) { console.log(error); @@ -387,7 +413,7 @@ //############################################## //传入预授权 URL ,将文件上传到这个地址 //############################################## - function uploadFile(file, preSignedUrl, fileId) { + function uploadFile(file, preSignedUrl, fileId, fileObjectId) { // refer: https://cloud.tencent.com/document/product/436/35651 // 获取到 Url 后,前端可以这样 ajax 上传 @@ -408,13 +434,16 @@ console.log('上传成功', xhr.status, xhr.statusText); // 等待进度条走到 100% 否则小文件进度条还没有走完就提示上传完成会让人感觉有点奇怪 setTimeout(function () { - alert("上传成功!"); - if ($("#checkbox-auto-upload").is(":checked")) { - window.location.reload(); - } else { - // location.href = "<%= pageUrl %>../"; - location.href = "<%= pageUrl %>../detail?id=" + fileId; - } + // 上传成功触发一次 “刷新文件对象上传状态”,避免因为云函数回调不成功导致文件对象上传状态没有及时更新 + refreshFileObjectStatus(fileObjectId, function () { + alert("上传成功!"); + if ($("#checkbox-auto-upload").is(":checked")) { + window.location.reload(); + } else { + // location.href = "<%= pageUrl %>../"; + location.href = "<%= pageUrl %>../detail?id=" + fileId; + } + }); }, 300); }; xhr.onerror = function (e) { @@ -428,7 +457,7 @@ function getFileAssociatorList(fileSha1) { var fileAssociator = document.getElementById("fileAssociator"); // 下拉框列表 - postRequest("/file/list/MatchfileHash", { token: localStorageUtils.getToken(), fileSha1: fileSha1 }) + postRequest("/file/list/MatchfileHashWithNullValue", { token: localStorageUtils.getToken(), fileSha1: fileSha1 }) .then(function (responseData) { var axiosData = responseData.data; var status = axiosData.status; @@ -452,4 +481,25 @@ alert("无法连接到服务器,请检查网络连接!"); }); } + + \ No newline at end of file diff --git a/bookshelfplus/src/main/java/plus/bookshelf/Common/Error/BusinessErrorCode.java b/bookshelfplus/src/main/java/plus/bookshelf/Common/Error/BusinessErrorCode.java index 7ce956f..a163872 100644 --- a/bookshelfplus/src/main/java/plus/bookshelf/Common/Error/BusinessErrorCode.java +++ b/bookshelfplus/src/main/java/plus/bookshelf/Common/Error/BusinessErrorCode.java @@ -28,6 +28,9 @@ public enum BusinessErrorCode implements CommonError { BOOK_FAVORITES_ALREADY_EXIST(50002, "书籍已经在收藏夹中"), BOOK_FAVORITES_NOT_EXIST(50003, "书籍不在收藏夹中"), + // 60000开头为文件、文件对象相关错误定义 + FILE_ALREADY_EXIST(60001, "文件已存在"), + // 占位 PLACE_HOLDER(99999, "这是一个占位符错误"); diff --git a/bookshelfplus/src/main/java/plus/bookshelf/Controller/Controller/FileController.java b/bookshelfplus/src/main/java/plus/bookshelf/Controller/Controller/FileController.java index aad4af6..23dd19c 100644 --- a/bookshelfplus/src/main/java/plus/bookshelf/Controller/Controller/FileController.java +++ b/bookshelfplus/src/main/java/plus/bookshelf/Controller/Controller/FileController.java @@ -100,10 +100,10 @@ public class FileController extends BaseController { } @ApiOperation(value = "【管理员】查询文件列表(匹配文件哈希)", notes = "查询文件列表,返回文件哈希为空或者相同的文件") - @RequestMapping(value = "list/MatchfileHash", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED}) + @RequestMapping(value = "list/MatchfileHashWithNullValue", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED}) @ResponseBody - public CommonReturnType matchfileHash(@RequestParam(value = "token", required = false) String token, - @RequestParam(value = "fileSha1", required = true) String fileSha1) throws InvocationTargetException, IllegalAccessException, BusinessException { + public CommonReturnType matchfileHashWithNullValue(@RequestParam(value = "token", required = false) String token, + @RequestParam(value = "fileSha1", required = true) String fileSha1) throws InvocationTargetException, IllegalAccessException, BusinessException { UserModel userModel = userService.getUserByToken(redisTemplate, token); if (userModel == null || !Objects.equals(userModel.getGroup(), "ADMIN")) { @@ -119,6 +119,22 @@ public class FileController extends BaseController { return CommonReturnType.create(fileVOS); } + @ApiOperation(value = "【管理员】通过文件SHA1哈希查找文件Id", notes = "查询文件列表,返回文件哈希匹配的文件Id") + @RequestMapping(value = "getFileByHash", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED}) + @ResponseBody + public CommonReturnType getFileByHash(@RequestParam(value = "token", required = false) String token, + @RequestParam(value = "fileSha1", required = true) String fileSha1) throws InvocationTargetException, IllegalAccessException, BusinessException { + + UserModel userModel = userService.getUserByToken(redisTemplate, token); + if (userModel == null || !Objects.equals(userModel.getGroup(), "ADMIN")) { + throw new BusinessException(BusinessErrorCode.OPERATION_NOT_ALLOWED, "非管理员用户无权进行此操作"); + } + + FileModel fileModel = fileService.selectBySha1(fileSha1); + FileVO fileVO = convertFileVOFromModel(fileModel); + return CommonReturnType.create(fileVO); + } + /** * 创建文件操作预授权URL * @@ -182,13 +198,17 @@ public class FileController extends BaseController { switch (httpMethodName) { case PUT: // 上传文件 - if (isExist) throw new BusinessException(BusinessErrorCode.PARAMETER_VALIDATION_ERROR, "文件已存在"); + if (isExist) throw new BusinessException(BusinessErrorCode.FILE_ALREADY_EXIST, "文件已存在"); - Integer realFileId = fileObjectService.uploadFile(fileId, fileName, bookSaveFolder + fileSha1, fileSize, + Integer[] integers = fileObjectService.uploadFile(fileId, fileName, bookSaveFolder + fileSha1, fileSize, fileSha1, fileExt, FileStorageMediumEnum.QCLOUD_COS, "", lastModified); + Integer realFileId = integers[0]; + Integer fileObjectId = integers[1]; + // fileId 可能为 0 (创建新文件) // realFileId 是从数据库中查询出来的,真实的文件id resultMap.put("fileId", realFileId); + resultMap.put("fileObjectId", fileObjectId); url = qCloudCosUtils.generatePresignedUrl(userModel.getId(), httpMethodName, bookSaveFolder, fileSha1, expireMinute, urlGUID); break; case GET: diff --git a/bookshelfplus/src/main/java/plus/bookshelf/Dao/Mapper/FileDOMapper.java b/bookshelfplus/src/main/java/plus/bookshelf/Dao/Mapper/FileDOMapper.java index 288b228..0865bb5 100644 --- a/bookshelfplus/src/main/java/plus/bookshelf/Dao/Mapper/FileDOMapper.java +++ b/bookshelfplus/src/main/java/plus/bookshelf/Dao/Mapper/FileDOMapper.java @@ -61,12 +61,19 @@ public interface FileDOMapper { FileDO[] selectAll(); /** - * 查询系统中的所有文件 + * 查询系统中所有 SHA1匹配 和 未设置SHA1 的文件 * * @return */ FileDO[] selectBySha1WithNullValue(String fileSha1); + /** + * 查询系统中一个 SHA1匹配 的文件 + * + * @return + */ + FileDO selectBySha1(String fileSha1); + /** * 列出文件支持的下载方式 * diff --git a/bookshelfplus/src/main/java/plus/bookshelf/Dao/Mapper/FileObjectDOMapper.java b/bookshelfplus/src/main/java/plus/bookshelf/Dao/Mapper/FileObjectDOMapper.java index 528f629..4ee2b79 100644 --- a/bookshelfplus/src/main/java/plus/bookshelf/Dao/Mapper/FileObjectDOMapper.java +++ b/bookshelfplus/src/main/java/plus/bookshelf/Dao/Mapper/FileObjectDOMapper.java @@ -75,4 +75,11 @@ public interface FileObjectDOMapper { * @return */ FileObjectDO[] selectFileObjectByBookId(Integer bookId); + + /** + * 获取上一次插入的主键Id + * + * @return + */ + int getLastInsertId(); } \ No newline at end of file diff --git a/bookshelfplus/src/main/java/plus/bookshelf/Service/Impl/FileObjectServiceImpl.java b/bookshelfplus/src/main/java/plus/bookshelf/Service/Impl/FileObjectServiceImpl.java index 7984f6a..d4e23c3 100644 --- a/bookshelfplus/src/main/java/plus/bookshelf/Service/Impl/FileObjectServiceImpl.java +++ b/bookshelfplus/src/main/java/plus/bookshelf/Service/Impl/FileObjectServiceImpl.java @@ -116,7 +116,7 @@ public class FileObjectServiceImpl implements FileObjectService { * @param fileSize 文件大小 * @param fileSHA1 文件SHA1 * @param fileExt 文件扩展名 - * @param fileNameWithoutExt 文件名(不包含扩展名) + * @param fileName 文件名(不包含扩展名) * @param fileStorageMediumEnum 文件存储介质 * @param source 文件来源 * @return 返回文件Id @@ -126,9 +126,9 @@ public class FileObjectServiceImpl implements FileObjectService { */ @Override @Transactional - public Integer uploadFile(Integer fileId, String fileName, String filePath, Long fileSize, String fileSHA1, - String fileExt, FileStorageMediumEnum fileStorageMediumEnum, - String source, Long lastModified + public Integer[] uploadFile(Integer fileId, String fileName, String filePath, Long fileSize, String fileSHA1, + String fileExt, FileStorageMediumEnum fileStorageMediumEnum, + String source, Long lastModified ) throws InvocationTargetException, IllegalAccessException, BusinessException { if (fileId == 0) { @@ -179,7 +179,11 @@ public class FileObjectServiceImpl implements FileObjectService { if (!isSuccess) { throw new BusinessException(BusinessErrorCode.UNKNOWN_ERROR, "文件对象创建失败"); } - return fileId; + + int lastInsertId = fileObjectDOMapper.getLastInsertId(); + + // fileId, fileObjectId + return new Integer[]{fileId, lastInsertId}; } /** diff --git a/bookshelfplus/src/main/java/plus/bookshelf/Service/Impl/FileServiceImpl.java b/bookshelfplus/src/main/java/plus/bookshelf/Service/Impl/FileServiceImpl.java index ea33b98..6c4fc22 100644 --- a/bookshelfplus/src/main/java/plus/bookshelf/Service/Impl/FileServiceImpl.java +++ b/bookshelfplus/src/main/java/plus/bookshelf/Service/Impl/FileServiceImpl.java @@ -8,11 +8,9 @@ import plus.bookshelf.Common.Error.BusinessErrorCode; import plus.bookshelf.Common.Error.BusinessException; import plus.bookshelf.Config.QCloudCosConfig; import plus.bookshelf.Dao.DO.FileDO; -import plus.bookshelf.Dao.DO.FileObjectDO; import plus.bookshelf.Dao.Mapper.FileDOMapper; import plus.bookshelf.Dao.Mapper.FileObjectDOMapper; import plus.bookshelf.Service.Model.FileModel; -import plus.bookshelf.Service.Model.FileObjectModel; import plus.bookshelf.Service.Service.CosPresignedUrlGenerateLogService; import plus.bookshelf.Service.Service.FileService; @@ -102,6 +100,18 @@ public class FileServiceImpl implements FileService { return fileModels; } + /** + * 列出一个 SHA1匹配 的文件 + * + * @return + */ + @Override + public FileModel selectBySha1(String fileSha1) { + FileDO fileDO = fileDOMapper.selectBySha1(fileSha1); + FileModel fileModel = convertFromDataObject(fileDO); + return fileModel; + } + private FileModel convertFromDataObject(FileDO fileDO) { if (fileDO == null) { return null; diff --git a/bookshelfplus/src/main/java/plus/bookshelf/Service/Service/FileObjectService.java b/bookshelfplus/src/main/java/plus/bookshelf/Service/Service/FileObjectService.java index d79a3b6..4996a6a 100644 --- a/bookshelfplus/src/main/java/plus/bookshelf/Service/Service/FileObjectService.java +++ b/bookshelfplus/src/main/java/plus/bookshelf/Service/Service/FileObjectService.java @@ -55,7 +55,7 @@ public interface FileObjectService { * @throws BusinessException */ @Transactional - Integer uploadFile(Integer fileId, String fileName, String filePath, Long fileSize, String fileSHA1, String fileExt, FileStorageMediumEnum fileStorageMediumEnum, String source, Long lastModified) throws InvocationTargetException, IllegalAccessException, BusinessException; + Integer[] uploadFile(Integer fileId, String fileName, String filePath, Long fileSize, String fileSHA1, String fileExt, FileStorageMediumEnum fileStorageMediumEnum, String source, Long lastModified) throws InvocationTargetException, IllegalAccessException, BusinessException; /** * 修改文件对象上传状态信息 diff --git a/bookshelfplus/src/main/java/plus/bookshelf/Service/Service/FileService.java b/bookshelfplus/src/main/java/plus/bookshelf/Service/Service/FileService.java index c7bb404..87daf1b 100644 --- a/bookshelfplus/src/main/java/plus/bookshelf/Service/Service/FileService.java +++ b/bookshelfplus/src/main/java/plus/bookshelf/Service/Service/FileService.java @@ -32,6 +32,14 @@ public interface FileService { */ List selectBySha1WithNullValue(String token) throws InvocationTargetException, IllegalAccessException, BusinessException; + /** + * 列出一个 SHA1匹配 的文件 + * + * @param fileSha1 + * @return + */ + FileModel selectBySha1(String fileSha1); + /** * 添加文件信息 * 返回是否添加成功 diff --git a/bookshelfplus/src/main/resources/mapping/FileDOMapper.xml b/bookshelfplus/src/main/resources/mapping/FileDOMapper.xml index 4208e53..8bc020e 100644 --- a/bookshelfplus/src/main/resources/mapping/FileDOMapper.xml +++ b/bookshelfplus/src/main/resources/mapping/FileDOMapper.xml @@ -223,6 +223,12 @@ from file_info where file_sha1 = #{fileSha1,jdbcType=VARCHAR} or file_sha1 is null or file_sha1 = '' + diff --git a/bookshelfplus/src/main/resources/mapping/FileObjectDOMapper.xml b/bookshelfplus/src/main/resources/mapping/FileObjectDOMapper.xml index 94e60d8..8f5194c 100644 --- a/bookshelfplus/src/main/resources/mapping/FileObjectDOMapper.xml +++ b/bookshelfplus/src/main/resources/mapping/FileObjectDOMapper.xml @@ -176,4 +176,7 @@ WHERE file_id IN (SELECT id AS file_id FROM `file_info` WHERE book_id = 1) AND upload_status = 'SUCCESS' + \ No newline at end of file