diff --git a/module-file/src/main/java/ink/wgink/module/file/components/FileComponent.java b/module-file/src/main/java/ink/wgink/module/file/components/FileComponent.java index 3bdf5664..b2d14605 100644 --- a/module-file/src/main/java/ink/wgink/module/file/components/FileComponent.java +++ b/module-file/src/main/java/ink/wgink/module/file/components/FileComponent.java @@ -1,10 +1,8 @@ package ink.wgink.module.file.components; import ink.wgink.exceptions.FileException; -import ink.wgink.exceptions.SearchException; import ink.wgink.exceptions.base.SystemException; import ink.wgink.module.file.enums.UploadTypeEnum; -import ink.wgink.module.file.service.IFileService; import ink.wgink.properties.FileProperties; import ink.wgink.util.date.DateUtil; import org.apache.commons.lang3.StringUtils; @@ -14,12 +12,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; /** * @ClassName: FileUtil @@ -32,106 +24,13 @@ import java.security.NoSuchAlgorithmException; public class FileComponent { protected static final Logger LOG = LoggerFactory.getLogger(FileComponent.class); - private static final char[] HEX_CODE = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; - private String[] imageTypes; - private String[] videoTypes; - private String[] audioTypes; - private String[] fileTypes; + private String[] imageTypes = null; + private String[] videoTypes = null; + private String[] audioTypes = null; + private String[] fileTypes = null; @Autowired private FileProperties fileProperties; - /** - * 保存文件 - * - * @param fileInputStream - * @param fileName - * @param filePath - * @return - */ - public long saveFile(InputStream fileInputStream, String fileName, String filePath) { - File uploadFolder = new File(filePath); - if (!uploadFolder.exists()) { - uploadFolder.mkdirs(); - } - FileOutputStream uploadFileOutputStream = null; - long fileSize = 0; - try { - uploadFileOutputStream = new FileOutputStream(uploadFolder + File.separator + fileName); - int readLength; - for (byte[] buf = new byte[IFileService.INPUT_STREAM_SIZE]; (readLength = fileInputStream.read(buf)) > -1; ) { - uploadFileOutputStream.write(buf, 0, readLength); - fileSize += readLength; - } - uploadFileOutputStream.flush(); - } catch (Exception e) { - LOG.error(e.getMessage(), e); - throw new FileException("文件上传失败"); - } finally { - try { - if (null != uploadFileOutputStream) { - uploadFileOutputStream.close(); - } - if (null != fileInputStream) { - fileInputStream.close(); - } - } catch (Exception e1) { - LOG.error(e1.getMessage()); - throw new FileException("文件上传失败"); - } - } - return fileSize; - } - - /** - * 删除源文件 - * - * @param sourceFilePath - */ - public static void deleteSourceFile(String sourceFilePath) { - File file = new File(sourceFilePath); - if (file.exists()) { - boolean isDelete = file.delete(); - if (isDelete) { - LOG.debug("文件删除成功"); - } else { - LOG.debug("文件删除失败"); - } - } - } - - /** - * 获取文件的MD5 - * - * @param file - * @return - */ - public String getFileMD5(File file) { - if (file == null) { - throw new SearchException("文件不存在"); - } - if (!file.exists()) { - throw new SearchException("文件不存在"); - } - String fileMd5; - try (InputStream inputStream = Files.newInputStream(file.toPath())) { - MessageDigest messageDigest = MessageDigest.getInstance("MD5"); - int readLength; - for (byte[] buf = new byte[IFileService.INPUT_STREAM_SIZE]; (readLength = inputStream.read(buf)) > -1; ) { - messageDigest.update(buf, 0, readLength); - } - // 计算文件的MD5 - byte[] data = messageDigest.digest(); - StringBuilder fileMd5SB = new StringBuilder(data.length * 2); - for (byte b : data) { - fileMd5SB.append(HEX_CODE[(b >> 4) & 0xF]); - fileMd5SB.append(HEX_CODE[(b & 0xF)]); - } - fileMd5 = fileMd5SB.toString(); - } catch (IOException | NoSuchAlgorithmException e) { - throw new SystemException(e); - } - return fileMd5; - } /** * 获取上传绝对文件 diff --git a/module-file/src/main/java/ink/wgink/module/file/controller/api/fileclient/FileClientController.java b/module-file/src/main/java/ink/wgink/module/file/controller/api/fileclient/FileClientController.java new file mode 100644 index 00000000..aefabbc1 --- /dev/null +++ b/module-file/src/main/java/ink/wgink/module/file/controller/api/fileclient/FileClientController.java @@ -0,0 +1,104 @@ +package ink.wgink.module.file.controller.api.fileclient; + +import ink.wgink.annotation.CheckRequestBodyAnnotation; +import ink.wgink.common.base.DefaultBaseController; +import ink.wgink.exceptions.RemoveException; +import ink.wgink.exceptions.SearchException; +import ink.wgink.interfaces.consts.ISystemConstant; +import ink.wgink.module.file.pojo.dtos.fileclient.FileClientDTO; +import ink.wgink.module.file.pojo.vos.fileremote.FileClientVO; +import ink.wgink.module.file.service.fileclient.IFileClientService; +import ink.wgink.pojo.ListPage; +import ink.wgink.pojo.result.ErrorResult; +import ink.wgink.pojo.result.SuccessResult; +import ink.wgink.pojo.result.SuccessResultList; +import io.swagger.annotations.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * @ClassName: FileClientController + * @Description: 文件远程 + * @Author: wanggeng + * @Date: 2022/8/4 22:18 + * @Version: 1.0 + */ +@Api(tags = ISystemConstant.API_TAGS_SYSTEM_PREFIX + "文件客户端接口") +@RestController +@RequestMapping(ISystemConstant.API_PREFIX + "/file-client") +public class FileClientController extends DefaultBaseController { + + @Autowired + private IFileClientService fileRemoteService; + + @ApiOperation(value = "新增", notes = "新增接口") + @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)}) + @PostMapping("save") + @CheckRequestBodyAnnotation + public SuccessResult save(@RequestBody FileClientVO fileClientVO) { + fileRemoteService.save(fileClientVO); + return new SuccessResult(); + } + + @ApiOperation(value = "删除(id列表)", notes = "删除(id列表)接口") + @ApiImplicitParams({ + @ApiImplicitParam(name = "ids", value = "ID列表,用下划线分隔", paramType = "path", example = "1_2_3") + }) + @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)}) + @DeleteMapping("remove/{ids}") + public SuccessResult remove(@PathVariable("ids") String ids) throws RemoveException { + fileRemoteService.remove(Arrays.asList(ids.split("\\_"))); + return new SuccessResult(); + } + + @ApiOperation(value = "修改", notes = "修改接口") + @ApiImplicitParams({ + @ApiImplicitParam(name = "fileClientId", value = "主键", paramType = "path") + }) + @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)}) + @PutMapping("update/{fileClientId}") + @CheckRequestBodyAnnotation + public SuccessResult update(@PathVariable("fileClientId") String fileClientId, @RequestBody FileClientVO fileClientVO) { + fileRemoteService.update(fileClientId, fileClientVO); + return new SuccessResult(); + } + + @ApiOperation(value = "详情(通过ID)", notes = "详情(通过ID)接口") + @ApiImplicitParams({ + @ApiImplicitParam(name = "fileClientId", value = "主键", paramType = "path") + }) + @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)}) + @GetMapping("get/{fileClientId}") + public FileClientDTO get(@PathVariable("fileClientId") String fileClientId) { + return fileRemoteService.get(fileClientId); + } + + @ApiOperation(value = "列表", notes = "列表接口") + @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)}) + @GetMapping("list") + public List list() throws SearchException { + Map params = requestParams(); + return fileRemoteService.list(params); + } + + @ApiOperation(value = "分页列表", notes = "分页列表接口") + @ApiImplicitParams({ + @ApiImplicitParam(name = "page", value = "当前页码", paramType = "query", dataType = "int", defaultValue = "1"), + @ApiImplicitParam(name = "rows", value = "显示数量", paramType = "query", dataType = "int", defaultValue = "20"), + @ApiImplicitParam(name = "keywords", value = "关键字", paramType = "query", dataType = "String"), + @ApiImplicitParam(name = "startTime", value = "开始时间", paramType = "query", dataType = "String"), + @ApiImplicitParam(name = "endTime", value = "结束时间", paramType = "query", dataType = "String") + }) + @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)}) + @GetMapping("listpage") + public SuccessResultList> listPage(ListPage page) throws SearchException { + Map params = requestParams(); + page.setParams(params); + return fileRemoteService.listPage(page); + } + +} diff --git a/module-file/src/main/java/ink/wgink/module/file/controller/api/v2/FileController.java b/module-file/src/main/java/ink/wgink/module/file/controller/api/v2/FileController.java index 5f0104e7..610d4ac0 100644 --- a/module-file/src/main/java/ink/wgink/module/file/controller/api/v2/FileController.java +++ b/module-file/src/main/java/ink/wgink/module/file/controller/api/v2/FileController.java @@ -13,9 +13,18 @@ import ink.wgink.pojo.result.SuccessResultData; import ink.wgink.pojo.result.SuccessResultList; import io.swagger.annotations.*; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.core.io.InputStreamResource; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.*; +import org.springframework.web.client.RestTemplate; import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -45,6 +54,66 @@ public class FileController extends DefaultBaseController { return uploadSingle(file, UploadTypeEnum.FILE); } + @ApiOperation(value = "上传文件", notes = "上传文件接口") + @ApiImplicitParams({ + @ApiImplicitParam(name = "file", value = "文件name", paramType = "query") + }) + @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)}) + @PostMapping("upload-file-inputstream") + public SuccessResultData uploadFileInputStream(@RequestParam("file") MultipartFile file) throws IOException { + RestTemplate restTemplate = new RestTemplate(); + String url = "http://localhost:7008/study/api/file/v2/upload-file"; + MultiValueMap params = new LinkedMultiValueMap<>(); + InputStreamResource inputStreamResource = new InputStreamResource(file.getInputStream()) { + @Override + public String getFilename() { + return file.getOriginalFilename(); + } + + @Override + public long contentLength() { + return file.getSize(); + } + }; + params.add("file", inputStreamResource); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + HttpEntity> requestEntity = new HttpEntity<>(params, headers); + String result = restTemplate.postForObject(url, requestEntity, String.class); + System.out.println(result); + return new SuccessResultData<>(); + } + + @ApiOperation(value = "上传文件", notes = "上传文件接口") + @ApiImplicitParams({ + @ApiImplicitParam(name = "file", value = "文件name", paramType = "query") + }) + @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)}) + @PostMapping("upload-file-bytearray") + public SuccessResultData uploadFileByteArray(@RequestParam("file") MultipartFile file) throws IOException { + RestTemplate restTemplate = new RestTemplate(); + String url = "http://localhost:7008/study/api/file/v2/upload-file"; + MultiValueMap params = new LinkedMultiValueMap<>(); + ByteArrayResource fileAsResource = new ByteArrayResource(file.getBytes()) { + @Override + public String getFilename() { + return file.getOriginalFilename(); + } + + @Override + public long contentLength() { + return file.getSize(); + } + }; + params.add("file", fileAsResource); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + HttpEntity> requestEntity = new HttpEntity<>(params, headers); + String result = restTemplate.postForObject(url, requestEntity, String.class); + System.out.println(result); + return new SuccessResultData<>(); + } + @ApiOperation(value = "上传图片", notes = "上传图片接口") @ApiImplicitParams({ @ApiImplicitParam(name = "image", value = "文件name", paramType = "query") diff --git a/module-file/src/main/java/ink/wgink/module/file/controller/app/api/filecenter/FileCenterAppController.java b/module-file/src/main/java/ink/wgink/module/file/controller/app/api/filecenter/FileCenterAppController.java new file mode 100644 index 00000000..754e43dc --- /dev/null +++ b/module-file/src/main/java/ink/wgink/module/file/controller/app/api/filecenter/FileCenterAppController.java @@ -0,0 +1,114 @@ +package ink.wgink.module.file.controller.app.api.filecenter; + +import ink.wgink.common.base.DefaultBaseController; +import ink.wgink.exceptions.ParamsException; +import ink.wgink.interfaces.consts.ISystemConstant; +import ink.wgink.module.file.enums.UploadTypeEnum; +import ink.wgink.module.file.manager.FileRemoteManager; +import ink.wgink.module.file.pojo.dtos.v2.FileUploadSuccessDTO; +import ink.wgink.module.file.service.filecenter.IFileCenterService; +import ink.wgink.pojo.result.ErrorResult; +import ink.wgink.pojo.result.SuccessResultData; +import io.swagger.annotations.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +/** + * @ClassName: FileAppCenterController + * @Description: APP文件远程 + * @Author: WangGeng + * @Date: 2019-08-02 15:38 + * @Version: 1.0 + **/ +@Api(tags = ISystemConstant.API_TAGS_APP_PREFIX + "文件远程调用接口") +@RestController("fileAppCenterControllerV2") +@RequestMapping(ISystemConstant.APP_PREFIX + "/file-center") +public class FileCenterAppController extends DefaultBaseController { + + @Autowired + private IFileCenterService fileCenterService; + + @ApiOperation(value = "上传文件", notes = "上传文件接口") + @ApiImplicitParams({ + @ApiImplicitParam(name = "Access-Key", value = "Access-Key", paramType = "header"), + @ApiImplicitParam(name = "Access-Secret", value = "Access-Secret", paramType = "header"), + @ApiImplicitParam(name = "file", value = "文件", paramType = "form"), + @ApiImplicitParam(name = "creator", value = "创建人", paramType = "form") + }) + @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)}) + @PostMapping("upload-file") + public SuccessResultData uploadFile(@RequestHeader("Access-Key") String accessKey, + @RequestHeader("Access-Secret") String accessSecret, + @RequestParam("file") MultipartFile file, + @RequestParam("creator") String creator) { + checkKeyAndSecret(accessKey, accessSecret); + return new SuccessResultData<>(fileCenterService.uploadSingleByUserId(creator, file, UploadTypeEnum.FILE)); + } + + @ApiOperation(value = "上传图片", notes = "上传图片接口") + @ApiImplicitParams({ + @ApiImplicitParam(name = "Access-Key", value = "Access-Key", paramType = "header"), + @ApiImplicitParam(name = "Access-Secret", value = "Access-Secret", paramType = "header"), + @ApiImplicitParam(name = "image", value = "图片", paramType = "form"), + @ApiImplicitParam(name = "creator", value = "创建人", paramType = "form") + }) + @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)}) + @PostMapping("upload-image") + public SuccessResultData uploadImage(@RequestHeader("Access-Key") String accessKey, + @RequestHeader("Access-Secret") String accessSecret, + @RequestParam("image") MultipartFile image, + @RequestParam("creator") String creator) { + checkKeyAndSecret(accessKey, accessSecret); + return new SuccessResultData<>(fileCenterService.uploadSingleByUserId(creator, image, UploadTypeEnum.IMAGE)); + } + + @ApiOperation(value = "上传视频", notes = "上传视频接口") + @ApiImplicitParams({ + @ApiImplicitParam(name = "Access-Key", value = "Access-Key", paramType = "header"), + @ApiImplicitParam(name = "Access-Secret", value = "Access-Secret", paramType = "header"), + @ApiImplicitParam(name = "video", value = "视频", paramType = "form"), + @ApiImplicitParam(name = "creator", value = "创建人", paramType = "form") + }) + @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)}) + @PostMapping("upload-video") + public SuccessResultData uploadVideo(@RequestHeader("Access-Key") String accessKey, + @RequestHeader("Access-Secret") String accessSecret, + @RequestParam("video") MultipartFile video, + @RequestParam("creator") String creator) { + checkKeyAndSecret(accessKey, accessSecret); + return new SuccessResultData<>(fileCenterService.uploadSingleByUserId(creator, video, UploadTypeEnum.VIDEO)); + } + + @ApiOperation(value = "上传音频", notes = "上传音频接口") + @ApiImplicitParams({ + @ApiImplicitParam(name = "Access-Key", value = "Access-Key", paramType = "header"), + @ApiImplicitParam(name = "Access-Secret", value = "Access-Secret", paramType = "header"), + @ApiImplicitParam(name = "audio", value = "音频", paramType = "form"), + @ApiImplicitParam(name = "creator", value = "创建人", paramType = "form") + }) + @ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)}) + @PostMapping("upload-audio") + public SuccessResultData uploadAudio(@RequestHeader("Access-Key") String accessKey, + @RequestHeader("Access-Secret") String accessSecret, + @RequestParam("audio") MultipartFile audio, + @RequestParam("creator") String creator) { + checkKeyAndSecret(accessKey, accessSecret); + return new SuccessResultData<>(fileCenterService.uploadSingleByUserId(creator, audio, UploadTypeEnum.AUDIO)); + } + + + + /** + * 校验Key与Secret + * + * @param accessKey + * @param accessSecret + */ + private void checkKeyAndSecret(String accessKey, String accessSecret) { + if (!FileRemoteManager.getInstance().check(accessKey, accessSecret)) { + throw new ParamsException("Key与Secret不匹配"); + } + } + +} diff --git a/module-file/src/main/java/ink/wgink/module/file/controller/route/fileclient/FileClientRouteController.java b/module-file/src/main/java/ink/wgink/module/file/controller/route/fileclient/FileClientRouteController.java new file mode 100644 index 00000000..3ee49738 --- /dev/null +++ b/module-file/src/main/java/ink/wgink/module/file/controller/route/fileclient/FileClientRouteController.java @@ -0,0 +1,40 @@ +package ink.wgink.module.file.controller.route.fileclient; + +import ink.wgink.interfaces.consts.ISystemConstant; +import io.swagger.annotations.Api; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.ModelAndView; + +/** + * @ClassName: FileRemoteRouteController + * @Description: 文件远程 + * @Author: wanggeng + * @Date: 2022/8/4 22:23 + * @Version: 1.0 + */ +@Api(tags = ISystemConstant.API_TAGS_SYSTEM_PREFIX + "文件远程") +@Controller +@RequestMapping(ISystemConstant.ROUTE_PREFIX + "/file-client") +public class FileClientRouteController { + + @GetMapping("list") + public ModelAndView list() { + ModelAndView modelAndView = new ModelAndView("file-client/list"); + return modelAndView; + } + + @GetMapping("save") + public ModelAndView save() { + ModelAndView modelAndView = new ModelAndView("file-client/save"); + return modelAndView; + } + + @GetMapping("update") + public ModelAndView update() { + ModelAndView modelAndView = new ModelAndView("file-client/update"); + return modelAndView; + } + +} diff --git a/module-file/src/main/java/ink/wgink/module/file/dao/fileclient/IFileClientDao.java b/module-file/src/main/java/ink/wgink/module/file/dao/fileclient/IFileClientDao.java new file mode 100644 index 00000000..ab59681b --- /dev/null +++ b/module-file/src/main/java/ink/wgink/module/file/dao/fileclient/IFileClientDao.java @@ -0,0 +1,65 @@ +package ink.wgink.module.file.dao.fileclient; + +import ink.wgink.exceptions.RemoveException; +import ink.wgink.exceptions.SaveException; +import ink.wgink.exceptions.SearchException; +import ink.wgink.exceptions.UpdateException; +import ink.wgink.interfaces.init.IInitBaseTable; +import ink.wgink.module.file.pojo.dtos.fileclient.FileClientDTO; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Map; + +/** + * @ClassName: IFileRemoteDao + * @Description: 文件远程 + * @Author: wanggeng + * @Date: 2022/8/4 20:53 + * @Version: 1.0 + */ +@Repository +public interface IFileClientDao extends IInitBaseTable { + + /** + * 新增 + * + * @param params + * @throws SaveException + */ + void save(Map params) throws SaveException; + + /** + * @param params + * @throws RemoveException + */ + void remove(Map params) throws RemoveException; + + + /** + * 修改状态 + * + * @param params + * @throws UpdateException + */ + void update(Map params) throws UpdateException; + + /** + * 详情 + * + * @param params + * @return + * @throws SearchException + */ + FileClientDTO get(Map params) throws SearchException; + + /** + * 列表 + * + * @param params + * @return + * @throws SearchException + */ + List list(Map params) throws SearchException; + +} diff --git a/module-file/src/main/java/ink/wgink/module/file/manager/FileRemoteManager.java b/module-file/src/main/java/ink/wgink/module/file/manager/FileRemoteManager.java new file mode 100644 index 00000000..9ef35841 --- /dev/null +++ b/module-file/src/main/java/ink/wgink/module/file/manager/FileRemoteManager.java @@ -0,0 +1,70 @@ +package ink.wgink.module.file.manager; + +import ink.wgink.module.file.dao.fileclient.IFileClientDao; +import ink.wgink.module.file.pojo.dtos.fileclient.FileClientDTO; +import org.apache.commons.lang3.StringUtils; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @ClassName: FileRemoteManager + * @Description: 文件远程管理 + * @Author: wanggeng + * @Date: 2022/8/4 23:16 + * @Version: 1.0 + */ +public class FileRemoteManager { + + private static FileRemoteManager fileRemoteManager = FileRemoteManagerBuilder.fileRemoteManager; + private Map updateKeyValueMap = new ConcurrentHashMap<>(); + private IFileClientDao fileRemoteDao; + + private FileRemoteManager() { + + } + + public static FileRemoteManager getInstance() { + return fileRemoteManager; + } + + public void refresh() { + updateKeyValueMap.clear(); + Map params = new HashMap<>(); + params.put("status", "active"); + List fileClientDTOS = fileRemoteDao.list(params); + for (FileClientDTO fileClientDTO : fileClientDTOS) { + updateKeyValueMap.put(fileClientDTO.getAccessKey(), fileClientDTO.getAccessSecret()); + } + } + + /** + * 校验 + * + * @param updateKey + * @param updateSecret + * @return + */ + public boolean check(String updateKey, String updateSecret) { + if (StringUtils.isBlank(updateKey) || StringUtils.isBlank(updateSecret)) { + return false; + } + String existUpdateSecret = updateKeyValueMap.get(updateKey); + if (!StringUtils.equals(updateSecret, existUpdateSecret)) { + return false; + } + return true; + } + + public void setFileRemoteDao(IFileClientDao fileRemoteDao) { + this.fileRemoteDao = fileRemoteDao; + refresh(); + } + + private static class FileRemoteManagerBuilder { + public static FileRemoteManager fileRemoteManager = new FileRemoteManager(); + } + +} diff --git a/module-file/src/main/java/ink/wgink/module/file/pojo/dtos/fileclient/FileClientDTO.java b/module-file/src/main/java/ink/wgink/module/file/pojo/dtos/fileclient/FileClientDTO.java new file mode 100644 index 00000000..012625ec --- /dev/null +++ b/module-file/src/main/java/ink/wgink/module/file/pojo/dtos/fileclient/FileClientDTO.java @@ -0,0 +1,76 @@ +package ink.wgink.module.file.pojo.dtos.fileclient; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +/** + * @ClassName: FileRemoteDTO + * @Description: 文件远程 + * @Author: wanggeng + * @Date: 2022/8/4 20:56 + * @Version: 1.0 + */ +@ApiModel +public class FileClientDTO { + + @ApiModelProperty(name = "fileClientId", value = "主键") + private String fileClientId; + @ApiModelProperty(name = "title", value = "标题") + private String title; + @ApiModelProperty(name = "accessKey", value = "上传Key") + private String accessKey; + @ApiModelProperty(name = "accessSecret", value = "上传秘钥") + private String accessSecret; + @ApiModelProperty(name = "status", value = "状态") + private String status; + @ApiModelProperty(name = "gmtCreate", value = "创建时间") + private String gmtCreate; + + public String getFileClientId() { + return fileClientId == null ? "" : fileClientId.trim(); + } + + public void setFileClientId(String fileClientId) { + this.fileClientId = fileClientId; + } + + public String getTitle() { + return title == null ? "" : title.trim(); + } + + public void setTitle(String title) { + this.title = title; + } + + public String getAccessKey() { + return accessKey == null ? "" : accessKey.trim(); + } + + public void setAccessKey(String accessKey) { + this.accessKey = accessKey; + } + + public String getAccessSecret() { + return accessSecret == null ? "" : accessSecret.trim(); + } + + public void setAccessSecret(String accessSecret) { + this.accessSecret = accessSecret; + } + + public String getStatus() { + return status == null ? "" : status.trim(); + } + + public void setStatus(String status) { + this.status = status; + } + + public String getGmtCreate() { + return gmtCreate == null ? "" : gmtCreate.trim(); + } + + public void setGmtCreate(String gmtCreate) { + this.gmtCreate = gmtCreate; + } +} diff --git a/module-file/src/main/java/ink/wgink/module/file/pojo/vos/fileremote/FileClientVO.java b/module-file/src/main/java/ink/wgink/module/file/pojo/vos/fileremote/FileClientVO.java new file mode 100644 index 00000000..82f8bcc5 --- /dev/null +++ b/module-file/src/main/java/ink/wgink/module/file/pojo/vos/fileremote/FileClientVO.java @@ -0,0 +1,39 @@ +package ink.wgink.module.file.pojo.vos.fileremote; + +import ink.wgink.annotation.CheckEmptyAnnotation; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +/** + * @ClassName: FileRemoteVO + * @Description: 文件远程 + * @Author: wanggeng + * @Date: 2022/8/4 21:35 + * @Version: 1.0 + */ +@ApiModel +public class FileClientVO { + + @ApiModelProperty(name = "title", value = "标题") + @CheckEmptyAnnotation(name = "标题") + private String title; + @ApiModelProperty(name = "status", value = "状态") + @CheckEmptyAnnotation(name = "状态") + private String status; + + public String getTitle() { + return title == null ? "" : title.trim(); + } + + public void setTitle(String title) { + this.title = title; + } + + public String getStatus() { + return status == null ? "" : status.trim(); + } + + public void setStatus(String status) { + this.status = status; + } +} diff --git a/module-file/src/main/java/ink/wgink/module/file/pojo/vos/v2/FileSaveVO.java b/module-file/src/main/java/ink/wgink/module/file/pojo/vos/v2/FileSaveVO.java index 04bc0296..2d80187b 100644 --- a/module-file/src/main/java/ink/wgink/module/file/pojo/vos/v2/FileSaveVO.java +++ b/module-file/src/main/java/ink/wgink/module/file/pojo/vos/v2/FileSaveVO.java @@ -12,8 +12,10 @@ public class FileSaveVO { private String fileName; private String fileType; private String uploadPath; + private String fileUrl; private long fileSize; private String creator; + private String fileSummary; public String getFileName() { return fileName == null ? "" : fileName.trim(); @@ -39,6 +41,14 @@ public class FileSaveVO { this.uploadPath = uploadPath; } + public String getFileUrl() { + return fileUrl == null ? "" : fileUrl.trim(); + } + + public void setFileUrl(String fileUrl) { + this.fileUrl = fileUrl; + } + public long getFileSize() { return fileSize; } @@ -54,4 +64,12 @@ public class FileSaveVO { public void setCreator(String creator) { this.creator = creator; } + + public String getFileSummary() { + return fileSummary == null ? "" : fileSummary.trim(); + } + + public void setFileSummary(String fileSummary) { + this.fileSummary = fileSummary; + } } diff --git a/module-file/src/main/java/ink/wgink/module/file/remote/IFileCenterRemoteService.java b/module-file/src/main/java/ink/wgink/module/file/remote/IFileCenterRemoteService.java new file mode 100644 index 00000000..85cb9d47 --- /dev/null +++ b/module-file/src/main/java/ink/wgink/module/file/remote/IFileCenterRemoteService.java @@ -0,0 +1,90 @@ +package ink.wgink.module.file.remote; + +import ink.wgink.annotation.rpc.rest.RemoteService; +import ink.wgink.annotation.rpc.rest.method.RemotePostMethod; +import ink.wgink.annotation.rpc.rest.params.RemoteFileInputStreamParams; +import ink.wgink.annotation.rpc.rest.params.RemoteFormParams; +import ink.wgink.annotation.rpc.rest.params.RemoteHeaderParams; +import ink.wgink.annotation.rpc.rest.params.RemoteServerParams; +import ink.wgink.common.rpc.rest.pojo.RemoteFileInputStream; +import ink.wgink.module.file.pojo.dtos.v2.FileUploadSuccessDTO; + +/** + * @ClassName: IFileCenterUploadService + * @Description: 文件中心上传 + * @Author: wanggeng + * @Date: 2022/8/4 15:00 + * @Version: 1.0 + */ +@RemoteService +public interface IFileCenterRemoteService { + + /** + * 上传文件 + * + * @param server + * @param accessKey + * @param accessSecret + * @param remoteFileInputStream + * @param creator + * @return + */ + @RemotePostMethod("/app/file-center/upload-file") + FileUploadSuccessDTO uploadFile(@RemoteServerParams String server, + @RemoteHeaderParams("Access-Key") String accessKey, + @RemoteHeaderParams("Access-Secret") String accessSecret, + @RemoteFileInputStreamParams("file") RemoteFileInputStream remoteFileInputStream, + @RemoteFormParams("creator") String creator); + + /** + * 上传图片 + * + * @param server + * @param accessKey + * @param accessSecret + * @param remoteFileInputStream + * @param creator + * @return + */ + @RemotePostMethod("/app/file-center/upload-image") + FileUploadSuccessDTO uploadImage(@RemoteServerParams String server, + @RemoteHeaderParams("Access-Key") String accessKey, + @RemoteHeaderParams("Access-Secret") String accessSecret, + @RemoteFileInputStreamParams("image") RemoteFileInputStream remoteFileInputStream, + @RemoteFormParams("creator") String creator); + + /** + * 上传音频 + * + * @param server + * @param accessKey + * @param accessSecret + * @param remoteFileInputStream + * @param creator + * @return + */ + @RemotePostMethod("/app/file-center/upload-audio") + FileUploadSuccessDTO uploadAudio(@RemoteServerParams String server, + @RemoteHeaderParams("Access-Key") String accessKey, + @RemoteHeaderParams("Access-Secret") String accessSecret, + @RemoteFileInputStreamParams("audio") RemoteFileInputStream remoteFileInputStream, + @RemoteFormParams("creator") String creator); + + /** + * 上传视频 + * + * @param server + * @param accessKey + * @param accessSecret + * @param remoteFileInputStream + * @param creator + * @return + */ + @RemotePostMethod("/app/file-center/upload-video") + FileUploadSuccessDTO uploadVideo(@RemoteServerParams String server, + @RemoteHeaderParams("Access-Key") String accessKey, + @RemoteHeaderParams("Access-Secret") String accessSecret, + @RemoteFileInputStreamParams("video") RemoteFileInputStream remoteFileInputStream, + @RemoteFormParams("creator") String creator); + +} diff --git a/module-file/src/main/java/ink/wgink/module/file/service/filecenter/IFileCenterService.java b/module-file/src/main/java/ink/wgink/module/file/service/filecenter/IFileCenterService.java new file mode 100644 index 00000000..1670bbdf --- /dev/null +++ b/module-file/src/main/java/ink/wgink/module/file/service/filecenter/IFileCenterService.java @@ -0,0 +1,27 @@ +package ink.wgink.module.file.service.filecenter; + +import ink.wgink.module.file.enums.UploadTypeEnum; +import ink.wgink.module.file.pojo.dtos.v2.FileUploadSuccessDTO; +import org.springframework.web.multipart.MultipartFile; + +/** + * @ClassName: IFileCenterService + * @Description: 文件中心 + * @Author: wanggeng + * @Date: 2022/8/6 13:36 + * @Version: 1.0 + */ +public interface IFileCenterService { + + /** + * 单文件上传 + * + * @param userId + * @param uploadFile + * @param uploadTypeEnum + * @return + */ + FileUploadSuccessDTO uploadSingleByUserId(String userId, MultipartFile uploadFile, UploadTypeEnum uploadTypeEnum); + + +} diff --git a/module-file/src/main/java/ink/wgink/module/file/service/filecenter/impl/FileCenterServiceImpl.java b/module-file/src/main/java/ink/wgink/module/file/service/filecenter/impl/FileCenterServiceImpl.java new file mode 100644 index 00000000..84465286 --- /dev/null +++ b/module-file/src/main/java/ink/wgink/module/file/service/filecenter/impl/FileCenterServiceImpl.java @@ -0,0 +1,116 @@ +package ink.wgink.module.file.service.filecenter.impl; + +import ink.wgink.common.base.DefaultBaseService; +import ink.wgink.exceptions.FileException; +import ink.wgink.exceptions.SearchException; +import ink.wgink.module.file.enums.UploadTypeEnum; +import ink.wgink.module.file.pojo.dtos.v2.FileUploadSuccessDTO; +import ink.wgink.module.file.pojo.vos.v2.FileSaveVO; +import ink.wgink.module.file.service.filecenter.IFileCenterService; +import ink.wgink.module.file.service.filedownload.IFileDownloadService; +import ink.wgink.module.file.service.v2.IFileService; +import ink.wgink.pojo.pos.FilePO; +import ink.wgink.util.request.StaticResourceRequestUtil; +import org.apache.catalina.connector.ClientAbortException; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.net.URLEncoder; +import java.nio.channels.Channels; +import java.nio.channels.FileChannel; +import java.nio.channels.WritableByteChannel; + +/** + * @ClassName: FileCenterServiceImpl + * @Description: 文件中心 + * @Author: wanggeng + * @Date: 2022/8/6 13:36 + * @Version: 1.0 + */ +@Service +public class FileCenterServiceImpl extends DefaultBaseService implements IFileCenterService { + + @Autowired + private IFileService fileService; + @Autowired + private IFileDownloadService fileDownloadService; + + @Override + public FileUploadSuccessDTO uploadSingleByUserId(String userId, MultipartFile uploadFile, UploadTypeEnum uploadTypeEnum) { + FileSaveVO fileSaveVO = getCenterFileSaveVO(uploadFile, uploadTypeEnum, userId); + return fileService.saveFile(fileSaveVO); + } + + private void download(HttpServletRequest request, HttpServletResponse response, String fileId) { + FilePO filePO = fileService.getPO(fileId); + try ( + RandomAccessFile randomAccessFile = new RandomAccessFile(filePO.getFilePath(), "r"); + FileChannel fileChannel = randomAccessFile.getChannel(); + OutputStream outputStream = response.getOutputStream(); + WritableByteChannel writableByteChannel = Channels.newChannel(outputStream); + ) { + response.setHeader("Content-Length", filePO.getFileSize().toString()); + response.setContentType(StaticResourceRequestUtil.getContentType(filePO.getFileType())); + response.setHeader("Content-Disposition", "inline;fileName=" + URLEncoder.encode(filePO.getFileName(), "UTF-8")); + String rangeString = request.getHeader("Range"); + long contentLength = filePO.getFileSize(); + long startRange = 0; + long endRange = contentLength - 1; + if (!StringUtils.isBlank(rangeString)) { + response.setContentType("multipart/byteranges"); + String[] rangeArray = rangeString.substring(rangeString.indexOf("=") + 1).split("-"); + startRange = Long.parseLong(rangeArray[0]); + if (rangeArray.length > 1) { + endRange = Long.parseLong(rangeArray[1]); + } + response.setHeader("Content-Length", String.valueOf(endRange - startRange + 1)); + response.setHeader("Content-Range", String.format("bytes %d-%d/%d", startRange, endRange, contentLength)); + response.setHeader("Accept-Ranges", "bytes"); + response.setHeader("Etag", fileId); + response.setStatus(206); + randomAccessFile.seek(startRange); + } + LOG.debug("startRange: {}, endRange: {}", startRange, endRange); + long totalOutputLength = endRange - startRange + 1; + fileChannel.transferTo(startRange, totalOutputLength, writableByteChannel); + outputStream.flush(); + if (endRange >= contentLength - 1) { + fileDownloadService.handle(request, false, fileId); + } + } catch (Exception e) { + e.printStackTrace(); + if (e instanceof ClientAbortException) { + LOG.debug("客户端断开连接"); + } else { + throw new FileException("文件输出异常", e); + } + } + } + + /** + * 获取文件保存VO + * + * @param uploadFile + * @param uploadTypeEnum + * @return + */ + private FileSaveVO getCenterFileSaveVO(MultipartFile uploadFile, UploadTypeEnum uploadTypeEnum, String creator) { + String fileName = uploadFile.getOriginalFilename(); + InputStream inputStream; + try { + inputStream = uploadFile.getInputStream(); + } catch (IOException e) { + throw new SearchException(e); + } + return fileService.getCenterFileSaveVO(inputStream, uploadTypeEnum, fileName, creator); + } + +} diff --git a/module-file/src/main/java/ink/wgink/module/file/service/fileclient/IFileClientService.java b/module-file/src/main/java/ink/wgink/module/file/service/fileclient/IFileClientService.java new file mode 100644 index 00000000..3d48f314 --- /dev/null +++ b/module-file/src/main/java/ink/wgink/module/file/service/fileclient/IFileClientService.java @@ -0,0 +1,73 @@ +package ink.wgink.module.file.service.fileclient; + +import ink.wgink.module.file.pojo.dtos.fileclient.FileClientDTO; +import ink.wgink.module.file.pojo.vos.fileremote.FileClientVO; +import ink.wgink.pojo.ListPage; +import ink.wgink.pojo.result.SuccessResultList; + +import java.util.List; +import java.util.Map; + +/** + * @ClassName: IFileRemoteService + * @Description: 文件远程 + * @Author: wanggeng + * @Date: 2022/8/4 21:32 + * @Version: 1.0 + */ +public interface IFileClientService { + + /** + * 保存 + * + * @param fileClientVO + */ + void save(FileClientVO fileClientVO); + + /** + * 删除 + * + * @param ids + */ + void remove(List ids); + + /** + * 更新状态 + * + * @param fileRemoteId + * @param fileClientVO + */ + void update(String fileRemoteId, FileClientVO fileClientVO); + + /** + * 详情 + * + * @param params + * @return + */ + FileClientDTO get(Map params); + + /** + * 详情 + * + * @param fileRemoteId + * @return + */ + FileClientDTO get(String fileRemoteId); + + /** + * 列表 + * + * @param params + * @return + */ + List list(Map params); + + /** + * 分页列表 + * + * @param page + * @return + */ + SuccessResultList> listPage(ListPage page); +} diff --git a/module-file/src/main/java/ink/wgink/module/file/service/fileclient/impl/FileClientServiceImpl.java b/module-file/src/main/java/ink/wgink/module/file/service/fileclient/impl/FileClientServiceImpl.java new file mode 100644 index 00000000..caa74c93 --- /dev/null +++ b/module-file/src/main/java/ink/wgink/module/file/service/fileclient/impl/FileClientServiceImpl.java @@ -0,0 +1,91 @@ +package ink.wgink.module.file.service.fileclient.impl; + +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import ink.wgink.common.base.DefaultBaseService; +import ink.wgink.module.file.dao.fileclient.IFileClientDao; +import ink.wgink.module.file.manager.FileRemoteManager; +import ink.wgink.module.file.pojo.dtos.fileclient.FileClientDTO; +import ink.wgink.module.file.pojo.vos.fileremote.FileClientVO; +import ink.wgink.module.file.service.fileclient.IFileClientService; +import ink.wgink.pojo.ListPage; +import ink.wgink.pojo.result.SuccessResultList; +import ink.wgink.util.UUIDUtil; +import ink.wgink.util.map.HashMapUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * @ClassName: FileRemoteServiceImpl + * @Description: 文件远程 + * @Author: wanggeng + * @Date: 2022/8/4 21:33 + * @Version: 1.0 + */ +@Service +public class FileClientServiceImpl extends DefaultBaseService implements IFileClientService { + + @Autowired + private IFileClientDao fileRemoteDao; + + @Override + public void save(FileClientVO fileClientVO) { + Map params = HashMapUtil.beanToMap(fileClientVO); + params.put("fileClientId", UUIDUtil.getUUID()); + params.put("accessKey", UUIDUtil.getUUID()); + params.put("accessSecret", UUIDUtil.get32UUID() + UUIDUtil.get32UUID()); + setSaveInfo(params); + params.put("isDelete", 0); + fileRemoteDao.save(params); + + FileRemoteManager.getInstance().refresh(); + } + + @Override + public void remove(List ids) { + Map params = getHashMap(2); + params.put("fileClientIds", ids); + fileRemoteDao.remove(params); + + FileRemoteManager.getInstance().refresh(); + } + + @Override + public void update(String fileClientId, FileClientVO fileClientVO) { + Map params = HashMapUtil.beanToMap(fileClientVO); + params.put("fileClientId", fileClientId); + setUpdateInfo(params); + fileRemoteDao.update(params); + + FileRemoteManager.getInstance().refresh(); + } + + @Override + public FileClientDTO get(Map params) { + params = params == null ? getHashMap(0) : params; + return fileRemoteDao.get(params); + } + + @Override + public FileClientDTO get(String fileClientId) { + Map params = getHashMap(2); + params.put("fileClientId", fileClientId); + return fileRemoteDao.get(params); + } + + @Override + public List list(Map params) { + return fileRemoteDao.list(params); + } + + @Override + public SuccessResultList> listPage(ListPage page) { + PageHelper.startPage(page.getPage(), page.getRows()); + List fileClientDTOS = list(page.getParams()); + PageInfo pageInfo = new PageInfo<>(fileClientDTOS); + return new SuccessResultList<>(fileClientDTOS, pageInfo.getPageNum(), pageInfo.getTotal()); + } +} diff --git a/module-file/src/main/java/ink/wgink/module/file/service/filedownload/IFileDownloadService.java b/module-file/src/main/java/ink/wgink/module/file/service/filedownload/IFileDownloadService.java index cfcaa8fb..78dc40ea 100644 --- a/module-file/src/main/java/ink/wgink/module/file/service/filedownload/IFileDownloadService.java +++ b/module-file/src/main/java/ink/wgink/module/file/service/filedownload/IFileDownloadService.java @@ -5,6 +5,7 @@ import ink.wgink.module.file.pojo.vos.filedownload.FileDownloadVO; import ink.wgink.pojo.ListPage; import ink.wgink.pojo.result.SuccessResultList; +import javax.servlet.http.HttpServletRequest; import java.util.List; import java.util.Map; @@ -66,4 +67,13 @@ public interface IFileDownloadService { */ Integer countByFileId(String fileId); + /** + * 下载 + * + * @param httpServletRequest + * @param isOpen 是否打开 + * @param fileId + */ + void handle(HttpServletRequest httpServletRequest, boolean isOpen, String fileId); + } diff --git a/module-file/src/main/java/ink/wgink/module/file/service/filedownload/impl/FileDownloadServiceImpl.java b/module-file/src/main/java/ink/wgink/module/file/service/filedownload/impl/FileDownloadServiceImpl.java index 2410b673..ee856608 100644 --- a/module-file/src/main/java/ink/wgink/module/file/service/filedownload/impl/FileDownloadServiceImpl.java +++ b/module-file/src/main/java/ink/wgink/module/file/service/filedownload/impl/FileDownloadServiceImpl.java @@ -12,9 +12,13 @@ import ink.wgink.pojo.result.SuccessResultList; import ink.wgink.util.UUIDUtil; import ink.wgink.util.date.DateUtil; import ink.wgink.util.map.HashMapUtil; +import ink.wgink.util.request.RequestUtil; +import ink.wgink.util.thread.CachedThreadPoolUtil; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import javax.servlet.http.HttpServletRequest; import java.util.List; import java.util.Map; @@ -73,5 +77,16 @@ public class FileDownloadServiceImpl extends DefaultBaseService implements IFile return fileDownloadDao.count(params); } + @Override + public void handle(HttpServletRequest httpServletRequest, boolean isOpen, String fileId) { + String requestIp = RequestUtil.getRequestIp(httpServletRequest); + if (!isOpen && !StringUtils.isBlank(requestIp)) { + // 记录下载历史 + CachedThreadPoolUtil.execute(() -> { + save(new FileDownloadVO(fileId, requestIp)); + }); + } + } + } diff --git a/module-file/src/main/java/ink/wgink/module/file/service/v2/IFileService.java b/module-file/src/main/java/ink/wgink/module/file/service/v2/IFileService.java index 1e21ca78..b1356776 100644 --- a/module-file/src/main/java/ink/wgink/module/file/service/v2/IFileService.java +++ b/module-file/src/main/java/ink/wgink/module/file/service/v2/IFileService.java @@ -25,6 +25,9 @@ import java.util.Map; */ public interface IFileService { + String FILE_SUMMARY_LOCAL = "local"; + String FILE_SUMMARY_CENTER = "center"; + /** * 单文件上传 * @@ -151,4 +154,14 @@ public interface IFileService { */ SuccessResultList> listPage(ListPage page); + /** + * 获取文件保存VO + * + * @param inputStream + * @param uploadTypeEnum + * @param fileName + * @return + */ + FileSaveVO getCenterFileSaveVO(InputStream inputStream, UploadTypeEnum uploadTypeEnum, String fileName, String creator); + } diff --git a/module-file/src/main/java/ink/wgink/module/file/service/v2/impl/FileServiceImpl.java b/module-file/src/main/java/ink/wgink/module/file/service/v2/impl/FileServiceImpl.java index cbdbbd29..3db4ff8d 100644 --- a/module-file/src/main/java/ink/wgink/module/file/service/v2/impl/FileServiceImpl.java +++ b/module-file/src/main/java/ink/wgink/module/file/service/v2/impl/FileServiceImpl.java @@ -3,6 +3,7 @@ package ink.wgink.module.file.service.v2.impl; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import ink.wgink.common.base.DefaultBaseService; +import ink.wgink.common.rpc.rest.pojo.RemoteFileInputStream; import ink.wgink.exceptions.FileException; import ink.wgink.exceptions.SearchException; import ink.wgink.module.file.components.FileComponent; @@ -11,18 +12,19 @@ import ink.wgink.module.file.enums.UploadTypeEnum; import ink.wgink.module.file.manager.FilesManager; import ink.wgink.module.file.pojo.dtos.FileInfoDTO; import ink.wgink.module.file.pojo.dtos.v2.FileUploadSuccessDTO; -import ink.wgink.module.file.pojo.vos.filedownload.FileDownloadVO; import ink.wgink.module.file.pojo.vos.v2.FileSaveVO; import ink.wgink.module.file.pojo.vos.v2.FileUpdateVO; +import ink.wgink.module.file.remote.IFileCenterRemoteService; import ink.wgink.module.file.service.filedownload.IFileDownloadService; import ink.wgink.module.file.service.v2.IFileService; +import ink.wgink.module.file.utils.FileUtil; import ink.wgink.pojo.ListPage; import ink.wgink.pojo.pos.FilePO; import ink.wgink.pojo.result.SuccessResultList; +import ink.wgink.properties.FileCenterProperties; +import ink.wgink.properties.FileProperties; import ink.wgink.util.UUIDUtil; import ink.wgink.util.date.DateUtil; -import ink.wgink.util.request.RequestUtil; -import ink.wgink.util.thread.CachedThreadPoolUtil; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -48,67 +50,65 @@ import java.util.Map; @Service("fileServiceV2") public class FileServiceImpl extends DefaultBaseService implements IFileService { + @Autowired + private FileProperties fileProperties; @Autowired private FileComponent fileComponent; @Autowired private IFileDao fileDao; @Autowired private IFileDownloadService fileDownloadService; + @Autowired + private IFileCenterRemoteService fileUploadCenterService; @Override public FileUploadSuccessDTO uploadSingle(MultipartFile uploadFile, UploadTypeEnum uploadTypeEnum) { - FileSaveVO fileSaveVO = getFileSaveVO(uploadFile, uploadTypeEnum); - fileSaveVO.setCreator(securityComponent.getCurrentUser().getUserId()); + FileSaveVO fileSaveVO = getCenterFileSaveVO(uploadFile, uploadTypeEnum, securityComponent.getCurrentUser().getUserId()); return saveFile(fileSaveVO); } @Override public FileUploadSuccessDTO uploadSingle(InputStream inputStream, UploadTypeEnum uploadTypeEnum, String fileName) { - FileSaveVO fileSaveVO = getFileSaveVO(inputStream, uploadTypeEnum, fileName); - fileSaveVO.setCreator(securityComponent.getCurrentUser().getUserId()); + FileSaveVO fileSaveVO = getCenterFileSaveVO(inputStream, uploadTypeEnum, fileName, securityComponent.getCurrentUser().getUserId()); return saveFile(fileSaveVO); } @Override public FileUploadSuccessDTO uploadSingleByToken(String token, MultipartFile uploadFile, UploadTypeEnum uploadTypeEnum) { - FileSaveVO fileSaveVO = getFileSaveVO(uploadFile, uploadTypeEnum); - fileSaveVO.setCreator(getAppTokenUser(token).getId()); + FileSaveVO fileSaveVO = getCenterFileSaveVO(uploadFile, uploadTypeEnum, getAppTokenUser(token).getId()); return saveFile(fileSaveVO); } @Override public FileUploadSuccessDTO uploadSingleByToken(String token, InputStream inputStream, UploadTypeEnum uploadTypeEnum, String fileName) { - FileSaveVO fileSaveVO = getFileSaveVO(inputStream, uploadTypeEnum, fileName); - fileSaveVO.setCreator(getAppTokenUser(token).getId()); + FileSaveVO fileSaveVO = getCenterFileSaveVO(inputStream, uploadTypeEnum, fileName, getAppTokenUser(token).getId()); return saveFile(fileSaveVO); } @Override public FileUploadSuccessDTO uploadSingleByUserId(String userId, MultipartFile uploadFile, UploadTypeEnum uploadTypeEnum) { - FileSaveVO fileSaveVO = getFileSaveVO(uploadFile, uploadTypeEnum); - fileSaveVO.setCreator(userId); + FileSaveVO fileSaveVO = getCenterFileSaveVO(uploadFile, uploadTypeEnum, userId); return saveFile(fileSaveVO); } @Override public FileUploadSuccessDTO uploadSingleByUserId(String userId, InputStream inputStream, UploadTypeEnum uploadTypeEnum, String fileName) { - FileSaveVO fileSaveVO = getFileSaveVO(inputStream, uploadTypeEnum, fileName); - fileSaveVO.setCreator(userId); + FileSaveVO fileSaveVO = getCenterFileSaveVO(inputStream, uploadTypeEnum, fileName, userId); return saveFile(fileSaveVO); } @Override public FileUploadSuccessDTO saveFile(FileSaveVO fileSaveVO) { - String fileUrl = fileComponent.getFileUrl(fileSaveVO.getUploadPath()); String fileId = UUIDUtil.getUUID(); Map params = getHashMap(10); params.put("fileId", fileId); params.put("fileName", fileSaveVO.getFileName()); params.put("filePath", fileSaveVO.getUploadPath()); - params.put("fileUrl", fileUrl); + params.put("fileUrl", fileSaveVO.getFileUrl()); params.put("fileType", fileSaveVO.getFileType()); params.put("fileSize", fileSaveVO.getFileSize()); + params.put("fileSummary", fileSaveVO.getFileSummary()); params.put("isBack", 0); String time = DateUtil.getTime(); @@ -119,7 +119,7 @@ public class FileServiceImpl extends DefaultBaseService implements IFileService params.put("isDelete", 0); fileDao.save(params); - return new FileUploadSuccessDTO(fileId, fileSaveVO.getFileName(), fileSaveVO.getFileSize(), fileUrl); + return new FileUploadSuccessDTO(fileId, fileSaveVO.getFileName(), fileSaveVO.getFileSize(), fileSaveVO.getFileUrl()); } @Override @@ -179,16 +179,9 @@ public class FileServiceImpl extends DefaultBaseService implements IFileService throw new FileException("文件不存在"); } try { - String requestIp = RequestUtil.getRequestIp(request); String code = FilesManager.getInstance().generateCode(fileId, filePO.getFileName()); response.sendRedirect(String.format("%s/%s?file=%s&code=%s&open=%d", request.getContextPath(), filePO.getFileUrl(), fileId, code, isOpen ? 1 : 0)); - if (!isOpen && !StringUtils.isBlank(requestIp)) { - // 记录下载历史 - CachedThreadPoolUtil.execute(() -> { - FileDownloadVO fileDownloadVO = new FileDownloadVO(filePO.getFileId(), requestIp); - fileDownloadService.save(fileDownloadVO); - }); - } + fileDownloadService.handle(request, isOpen, fileId); } catch (IOException e) { throw new RuntimeException(e); } @@ -212,6 +205,24 @@ public class FileServiceImpl extends DefaultBaseService implements IFileService return new SuccessResultList<>(fileInfoDTOs, pageInfo.getPageNum(), pageInfo.getTotal()); } + @Override + public FileSaveVO getCenterFileSaveVO(InputStream inputStream, UploadTypeEnum uploadTypeEnum, String fileName, String creator) { + String fileType = fileComponent.getFileType(fileName); + String uploadPath = fileComponent.getUploadPath(uploadTypeEnum, fileType); + String uuidFileName = UUIDUtil.get32UUID() + "." + fileType; + long fileSize = FileUtil.save(inputStream, uuidFileName, uploadPath); + // 保存文件 + FileSaveVO fileSaveVO = new FileSaveVO(); + fileSaveVO.setFileName(fileName); + fileSaveVO.setFileType(fileType); + fileSaveVO.setUploadPath(uploadPath + File.separator + uuidFileName); + fileSaveVO.setFileUrl(fileComponent.getFileUrl(fileSaveVO.getUploadPath())); + fileSaveVO.setFileSize(fileSize); + fileSaveVO.setCreator(creator); + fileSaveVO.setFileSummary(FILE_SUMMARY_LOCAL); + return fileSaveVO; + } + /** * 设置查询文件类型 * @@ -256,7 +267,7 @@ public class FileServiceImpl extends DefaultBaseService implements IFileService * @param uploadTypeEnum * @return */ - private FileSaveVO getFileSaveVO(MultipartFile uploadFile, UploadTypeEnum uploadTypeEnum) { + private FileSaveVO getCenterFileSaveVO(MultipartFile uploadFile, UploadTypeEnum uploadTypeEnum, String creator) { String fileName = uploadFile.getOriginalFilename(); InputStream inputStream; try { @@ -264,28 +275,66 @@ public class FileServiceImpl extends DefaultBaseService implements IFileService } catch (IOException e) { throw new SearchException(e); } - return getFileSaveVO(inputStream, uploadTypeEnum, fileName); + // 上传到文件中心 + if (fileProperties.getCenter() != null) { + return getCenterFileSaveVO(inputStream, uploadTypeEnum, fileName, uploadFile.getSize(), creator); + } + return getCenterFileSaveVO(inputStream, uploadTypeEnum, fileName, creator); } /** - * 获取文件保存VO + * 上传文件到文件中心 * * @param inputStream * @param uploadTypeEnum * @param fileName + * @param fileSize + * @param creator * @return */ - private FileSaveVO getFileSaveVO(InputStream inputStream, UploadTypeEnum uploadTypeEnum, String fileName) { + private FileSaveVO getCenterFileSaveVO(InputStream inputStream, UploadTypeEnum uploadTypeEnum, String fileName, long fileSize, String creator) { + RemoteFileInputStream remoteFileInputStream = new RemoteFileInputStream(); + remoteFileInputStream.setInputStream(inputStream); + remoteFileInputStream.setFileName(fileName); + remoteFileInputStream.setFileSize(fileSize); + String fileType = fileComponent.getFileType(fileName); - String uploadPath = fileComponent.getUploadPath(uploadTypeEnum, fileType); - String uuidFileName = UUIDUtil.get32UUID() + "." + fileType; - long fileSize = fileComponent.saveFile(inputStream, uuidFileName, uploadPath); - // 保存文件 + FileCenterProperties fileCenterProperties = fileProperties.getCenter(); + FileUploadSuccessDTO fileUploadSuccessDTO; + if (UploadTypeEnum.IMAGE.equals(uploadTypeEnum)) { + fileUploadSuccessDTO = fileUploadCenterService.uploadImage(fileCenterProperties.getUploadUrl(), + fileCenterProperties.getAccessKey(), + fileCenterProperties.getAccessSecret(), + remoteFileInputStream, + creator); + } else if (UploadTypeEnum.VIDEO.equals(uploadTypeEnum)) { + fileUploadSuccessDTO = fileUploadCenterService.uploadVideo(fileCenterProperties.getUploadUrl(), + fileCenterProperties.getAccessKey(), + fileCenterProperties.getAccessSecret(), + remoteFileInputStream, + creator); + } else if (UploadTypeEnum.AUDIO.equals(uploadTypeEnum)) { + fileUploadSuccessDTO = fileUploadCenterService.uploadAudio(fileCenterProperties.getUploadUrl(), + fileCenterProperties.getAccessKey(), + fileCenterProperties.getAccessSecret(), + remoteFileInputStream, + creator); + } else { + fileUploadSuccessDTO = fileUploadCenterService.uploadFile(fileCenterProperties.getUploadUrl(), + fileCenterProperties.getAccessKey(), + fileCenterProperties.getAccessSecret(), + remoteFileInputStream, + creator); + } + FileSaveVO fileSaveVO = new FileSaveVO(); - fileSaveVO.setFileName(fileName); + fileSaveVO.setUploadPath(fileCenterProperties.getUploadUrl()); + fileSaveVO.setFileUrl(fileUploadSuccessDTO.getFileUrl()); fileSaveVO.setFileType(fileType); - fileSaveVO.setUploadPath(uploadPath + File.separator + uuidFileName); fileSaveVO.setFileSize(fileSize); + fileSaveVO.setFileName(fileName); + fileSaveVO.setCreator(creator); + fileSaveVO.setFileSummary(FILE_SUMMARY_CENTER); return fileSaveVO; } diff --git a/module-file/src/main/java/ink/wgink/module/file/startup/FilesStartUp.java b/module-file/src/main/java/ink/wgink/module/file/startup/FilesStartUp.java index 7d0986b3..96acbf73 100644 --- a/module-file/src/main/java/ink/wgink/module/file/startup/FilesStartUp.java +++ b/module-file/src/main/java/ink/wgink/module/file/startup/FilesStartUp.java @@ -1,6 +1,8 @@ package ink.wgink.module.file.startup; import ink.wgink.interfaces.manager.IFilesShowCodeService; +import ink.wgink.module.file.dao.fileclient.IFileClientDao; +import ink.wgink.module.file.manager.FileRemoteManager; import ink.wgink.module.file.manager.FilesManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.ApplicationArguments; @@ -20,10 +22,13 @@ public class FilesStartUp implements ApplicationRunner { @Autowired private IFilesShowCodeService filesShowCodeService; + @Autowired + private IFileClientDao fileRemoteDao; @Override public void run(ApplicationArguments args) throws Exception { FilesManager.getInstance().setFilesShowCodeService(filesShowCodeService); + FileRemoteManager.getInstance().setFileRemoteDao(fileRemoteDao); } @Scheduled(cron = "0 0/1 * * * ?") diff --git a/module-file/src/main/java/ink/wgink/module/file/utils/FileUtil.java b/module-file/src/main/java/ink/wgink/module/file/utils/FileUtil.java new file mode 100644 index 00000000..5c8659ad --- /dev/null +++ b/module-file/src/main/java/ink/wgink/module/file/utils/FileUtil.java @@ -0,0 +1,119 @@ +package ink.wgink.module.file.utils; + +import ink.wgink.exceptions.FileException; +import ink.wgink.exceptions.SearchException; +import ink.wgink.exceptions.base.SystemException; +import ink.wgink.module.file.service.IFileService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * @ClassName: FileSaveUtil + * @Description: 保存文件工具 + * @Author: wanggeng + * @Date: 2022/8/3 21:12 + * @Version: 1.0 + */ +public class FileUtil { + + private static final Logger LOG = LoggerFactory.getLogger(FileUtil.class); + private static final char[] HEX_CODE = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + /** + * 保存文件 + * + * @param fileInputStream 文件流 + * @param fileName 文件名 + * @param filePath 保存路径 + * @return 文件大小 + */ + public static long save(InputStream fileInputStream, String fileName, String filePath) { + File uploadFolder = new File(filePath); + if (!uploadFolder.exists()) { + uploadFolder.mkdirs(); + } + FileOutputStream uploadFileOutputStream = null; + long fileSize = 0; + try { + uploadFileOutputStream = new FileOutputStream(uploadFolder + File.separator + fileName); + int readLength; + for (byte[] buf = new byte[IFileService.INPUT_STREAM_SIZE]; (readLength = fileInputStream.read(buf)) > -1; ) { + uploadFileOutputStream.write(buf, 0, readLength); + fileSize += readLength; + } + uploadFileOutputStream.flush(); + } catch (Exception e) { + LOG.error(e.getMessage(), e); + throw new FileException("文件上传失败"); + } finally { + try { + if (null != uploadFileOutputStream) { + uploadFileOutputStream.close(); + } + if (null != fileInputStream) { + fileInputStream.close(); + } + } catch (Exception e1) { + LOG.error(e1.getMessage()); + throw new FileException("文件上传失败"); + } + } + return fileSize; + } + + /** + * 删除源文件 + * + * @param sourceFilePath + */ + public static boolean delete(String sourceFilePath) { + File file = new File(sourceFilePath); + if (file.exists()) { + return file.delete(); + } + return true; + } + + /** + * 获取文件的MD5 + * + * @param file + * @return + */ + public static String getMD5(File file) { + if (file == null) { + throw new SearchException("文件不存在"); + } + if (!file.exists()) { + throw new SearchException("文件不存在"); + } + String fileMd5; + try (InputStream inputStream = Files.newInputStream(file.toPath())) { + MessageDigest messageDigest = MessageDigest.getInstance("MD5"); + int readLength; + for (byte[] buf = new byte[IFileService.INPUT_STREAM_SIZE]; (readLength = inputStream.read(buf)) > -1; ) { + messageDigest.update(buf, 0, readLength); + } + // 计算文件的MD5 + byte[] data = messageDigest.digest(); + StringBuilder fileMd5SB = new StringBuilder(data.length * 2); + for (byte b : data) { + fileMd5SB.append(HEX_CODE[(b >> 4) & 0xF]); + fileMd5SB.append(HEX_CODE[(b & 0xF)]); + } + fileMd5 = fileMd5SB.toString(); + } catch (IOException | NoSuchAlgorithmException e) { + throw new SystemException(e); + } + return fileMd5; + } + +} diff --git a/module-file/src/main/resources/mybatis/mapper/file-client/file-client-mapper.xml b/module-file/src/main/resources/mybatis/mapper/file-client/file-client-mapper.xml new file mode 100644 index 00000000..3edc8346 --- /dev/null +++ b/module-file/src/main/resources/mybatis/mapper/file-client/file-client-mapper.xml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + CREATE TABLE IF NOT EXISTS `sys_file_client` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `file_client_id` char(36) DEFAULT NULL COMMENT '主键', + `title` varchar(255) DEFAULT NULL COMMENT '标题', + `access_key` varchar(255) DEFAULT NULL COMMENT '上传Key', + `access_secret` varchar(255) DEFAULT NULL COMMENT '上传密码', + `status` varchar(255) DEFAULT NULL COMMENT '状态', + `gmt_create` datetime DEFAULT NULL COMMENT '创建时间', + `creator` char(36) DEFAULT NULL COMMENT '创建人', + `gmt_modified` datetime DEFAULT NULL COMMENT '修改时间', + `modifier` char(36) DEFAULT NULL COMMENT '修改人', + `is_delete` int(1) DEFAULT '0' COMMENT '是否删除', + PRIMARY KEY (`id`), + UNIQUE KEY `file_client_id` (`file_client_id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文件远程调用'; + + + + + INSERT INTO sys_file_client ( + file_client_id, + title, + access_key, + access_secret, + status, + gmt_create, + creator, + gmt_modified, + modifier, + is_delete + ) VALUES( + #{fileClientId}, + #{title}, + #{accessKey}, + #{accessSecret}, + #{status}, + #{gmtCreate}, + #{creator}, + #{gmtModified}, + #{modifier}, + #{isDelete} + ) + + + + + UPDATE + sys_file_client + SET + gmt_modified = #{gmtModified}, + modifier = #{modifier}, + is_delete = 0 + + + + + UPDATE + sys_file_client + SET + title = #{title}, + status = #{status}, + gmt_modified = #{gmtModified}, + modifier = #{modifier} + WHERE + file_client_id = #{fileClientId} + + + + + + + + + \ No newline at end of file diff --git a/module-file/src/main/resources/templates/file-client/list.html b/module-file/src/main/resources/templates/file-client/list.html new file mode 100644 index 00000000..5fd2f124 --- /dev/null +++ b/module-file/src/main/resources/templates/file-client/list.html @@ -0,0 +1,253 @@ + + + + + + + + + + + + + + +
+
+
+
+
+
+
+ +
+ +
+
+ + +
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/module-file/src/main/resources/templates/file-client/save.html b/module-file/src/main/resources/templates/file-client/save.html new file mode 100644 index 00000000..60066251 --- /dev/null +++ b/module-file/src/main/resources/templates/file-client/save.html @@ -0,0 +1,108 @@ + + + + + + + + + + + + + +
+
+ +
+
+
+ +
+ +
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+
+
+ + + + \ No newline at end of file diff --git a/module-file/src/main/resources/templates/file-client/update.html b/module-file/src/main/resources/templates/file-client/update.html new file mode 100644 index 00000000..3b85913d --- /dev/null +++ b/module-file/src/main/resources/templates/file-client/update.html @@ -0,0 +1,125 @@ + + + + + + + + + + + + + +
+
+ +
+
+
+ +
+ +
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+
+
+ + + + \ No newline at end of file