/* * Copyright (c) 2018-2028, Chill Zhuang All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * Neither the name of the dreamlu.net developer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * Author: Chill 庄骞 (smallchill@163.com) */ package org.springblade.modules.resource.endpoint; import com.alibaba.fastjson.JSONObject; import io.swagger.annotations.Api; import lombok.AllArgsConstructor; import lombok.SneakyThrows; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springblade.common.utils.ImageUtils; import org.springblade.common.utils.WeiXinSecurityUtil; import org.springblade.core.launch.constant.AppConstant; import org.springblade.core.oss.model.BladeFile; import org.springblade.core.oss.model.OssFile; import org.springblade.core.secure.annotation.PreAuth; import org.springblade.core.tenant.annotation.NonDS; import org.springblade.core.tool.api.R; import org.springblade.core.tool.constant.RoleConstant; import org.springblade.core.tool.utils.FileUtil; import org.springblade.core.tool.utils.Func; import org.springblade.modules.resource.builder.oss.OssBuilder; import org.springblade.modules.resource.entity.Attach; import org.springblade.modules.resource.entity.AttachData; import org.springblade.modules.resource.service.IAttachDataService; import org.springblade.modules.resource.service.IAttachService; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.util.Arrays; import java.util.List; /** * 对象存储端点 * * @author Chill */ @NonDS @RestController @AllArgsConstructor @Api(value = "对象存储端点", tags = "对象存储端点") @RequestMapping(AppConstant.APPLICATION_RESOURCE_NAME + "/oss/endpoint") public class OssEndpoint { private static final Logger logger = LoggerFactory.getLogger(OssEndpoint.class); /** * 对象存储构建类 */ private final OssBuilder ossBuilder; /** * 附件表服务 */ private final IAttachService attachService; /** * 附件数据表服务 */ private final IAttachDataService attachDataService; /** * 创建存储桶 * * @param bucketName 存储桶名称 * @return Bucket */ @SneakyThrows @PostMapping("/make-bucket") @PreAuth(RoleConstant.HAS_ROLE_ADMIN) public R makeBucket(@RequestParam String bucketName) { ossBuilder.template().makeBucket(bucketName); return R.success("创建成功"); } /** * 创建存储桶 * * @param bucketName 存储桶名称 * @return R */ @SneakyThrows @PostMapping("/remove-bucket") @PreAuth(RoleConstant.HAS_ROLE_ADMIN) public R removeBucket(@RequestParam String bucketName) { ossBuilder.template().removeBucket(bucketName); return R.success("删除成功"); } /** * 拷贝文件 * * @param fileName 存储桶对象名称 * @param destBucketName 目标存储桶名称 * @param destFileName 目标存储桶对象名称 * @return R */ @SneakyThrows @PostMapping("/copy-file") public R copyFile(@RequestParam String fileName, @RequestParam String destBucketName, String destFileName) { ossBuilder.template().copyFile(fileName, destBucketName, destFileName); return R.success("操作成功"); } /** * 获取文件信息 * * @param fileName 存储桶对象名称 * @return InputStream */ @SneakyThrows @GetMapping("/stat-file") public R statFile(@RequestParam String fileName) { return R.data(ossBuilder.template().statFile(fileName)); } /** * 获取文件相对路径 * * @param fileName 存储桶对象名称 * @return String */ @SneakyThrows @GetMapping("/file-path") public R filePath(@RequestParam String fileName) { return R.data(ossBuilder.template().filePath(fileName)); } /** * 获取文件外链 * * @param fileName 存储桶对象名称 * @return String */ @SneakyThrows @GetMapping("/file-link") public R fileLink(@RequestParam String fileName) { return R.data(ossBuilder.template().fileLink(fileName)); } /** * 上传文件 * * @param file 文件 * @return ObjectStat */ @SneakyThrows @PostMapping("/put-file") public R putFile(@RequestParam MultipartFile file) { BladeFile bladeFile = ossBuilder.template().putFile(file.getOriginalFilename(), file.getInputStream()); // 修改link changeLink(bladeFile); // 返回 return R.data(200, bladeFile, ""); } /** * 上传文件 * * @param file 文件 * @return ObjectStat */ @SneakyThrows @PostMapping("/put-pdfFile") public R putPdfFile(@RequestParam MultipartFile file) { String originalFilename = file.getOriginalFilename(); String fileExtension = originalFilename.substring(originalFilename.lastIndexOf(".") + 1); if (fileExtension.equalsIgnoreCase("pdf")) { BladeFile bladeFile = ossBuilder.template().putFile(file.getOriginalFilename(), file.getInputStream()); // 修改link changeLink(bladeFile); // 返回 return R.data(200, bladeFile, ""); } else { return R.fail("上传的附件必须是pdf格式!"); } } /** * 修改link * * @param bladeFile */ private void changeLink(BladeFile bladeFile) { if (null != bladeFile) { // 替换url link 前缀 String newLink = ossBuilder.ossProperties().getEndpoint() + bladeFile.getName(); bladeFile.setLink(newLink); } } /** * 上传文件 * * @param fileName 存储桶对象名称 * @param file 文件 * @return ObjectStat */ @SneakyThrows @PostMapping("/put-file-by-name") public R putFile(@RequestParam String fileName, @RequestParam MultipartFile file) { BladeFile bladeFile = ossBuilder.template().putFile(fileName, file.getInputStream()); return R.data(bladeFile); } /** * 上传文件并保存至附件表 * * @param file 文件 * @return ObjectStat */ @SneakyThrows @PostMapping("/put-file-attach") public R putFileAttach(@RequestParam MultipartFile file) { String fileName = file.getOriginalFilename(); BladeFile bladeFile = ossBuilder.template().putFile(fileName, file.getInputStream()); Long attachId = buildAttach(fileName, file.getSize(), bladeFile); bladeFile.setAttachId(attachId); // 修改link changeLink(bladeFile); // 返回 return R.data(bladeFile); } /** * 上传文件指定后缀并保存至附件表 * * @param file 文件 * @return ObjectStat */ @SneakyThrows @PostMapping("/put-file-attach-suffix") public R putFileAttachSuffix(@RequestParam MultipartFile file, @RequestParam String suffix) { String fileName = file.getOriginalFilename(); String fileExtension = fileName.substring(fileName.lastIndexOf(".") + 1); String[] split = suffix.split(","); List list = Arrays.asList(split); if (list.contains(fileExtension)) { BladeFile bladeFile = ossBuilder.template().putFile(fileName, file.getInputStream()); Long attachId = buildAttach(fileName, file.getSize(), bladeFile); bladeFile.setAttachId(attachId); // 修改link changeLink(bladeFile); // 返回 return R.data(bladeFile); } else { return R.fail("上传的附件必须是pdf,doc,docx格式!"); } } /** * 上传文件并保存至附件表 * * @param fileName 存储桶对象名称 * @param file 文件 * @return ObjectStat */ @SneakyThrows @PostMapping("/put-file-attach-by-name") public R putFileAttach(@RequestParam(required = false) String fileName, @RequestParam MultipartFile file) { BladeFile bladeFile = ossBuilder.template().putFile(fileName, file.getInputStream()); Long attachId = buildAttach(fileName, file.getSize(), bladeFile); bladeFile.setAttachId(attachId); return R.data(bladeFile); } /** * 自定义前缀上传文件 * * @param file 文件 * @param prefixPath 文件 * @param isCheck 是否校验 * @return ObjectStat */ @SneakyThrows @PostMapping("/put-file-by-prefix-path") public R putFileByPrefixPath(@RequestParam MultipartFile file, @RequestParam(required = false) String prefixPath, @RequestParam(required = false) Integer isCheck) { // 不传默认不检验 if (null != isCheck) { String checkResult = WeiXinSecurityUtil.checkImg(file); JSONObject jsonObject = JSONObject.parseObject(checkResult); Integer errorCode = Integer.parseInt(jsonObject.getString("errcode")); if (errorCode != 0) { return R.data(201, null, "图片存在敏感内容,请更换其他图片!"); } } BladeFile bladeFile = ossBuilder.templateByPrefixPath(prefixPath).putFile(file.getOriginalFilename(), file.getInputStream()); // 修改link changeLink(bladeFile); return R.data(200, bladeFile, ""); } /** * 自定义前缀上传文件并保存至附件表 * * @param file 文件 * @param prefixPath 文件 * @param isCheck 是否校验 * @return ObjectStat */ @SneakyThrows @PostMapping("/put-file-attach-by-prefix-path") public R putFileAttachByPrefixPath(@RequestParam MultipartFile file, @RequestParam(required = false) String prefixPath, @RequestParam(required = false) Integer isCheck) { // 不传默认不检验 if (null != isCheck) { String checkResult = WeiXinSecurityUtil.checkImg(file); JSONObject jsonObject = JSONObject.parseObject(checkResult); Integer errorCode = Integer.parseInt(jsonObject.getString("errcode")); if (errorCode != 0) { return R.data(201, null, "图片存在敏感内容,请更换其他图片!"); } } String fileName = file.getOriginalFilename(); BladeFile bladeFile = ossBuilder.templateByPrefixPath(prefixPath).putFile(file.getOriginalFilename(), file.getInputStream()); Long attachId = buildAttach(fileName, file.getSize(), bladeFile); buildAttachData(attachId, fileName, file.getSize(), bladeFile, file); bladeFile.setAttachId(attachId); // 修改link changeLink(bladeFile); return R.data(200, bladeFile, ""); } /** * 构建附件表 * * @param fileName 文件名 * @param fileSize 文件大小 * @param bladeFile 对象存储文件 * @return attachId */ private Long buildAttach(String fileName, Long fileSize, BladeFile bladeFile) { String fileExtension = FileUtil.getFileExtension(fileName); Attach attach = new Attach(); attach.setDomainUrl(bladeFile.getDomain()); attach.setLink(bladeFile.getLink()); attach.setName(bladeFile.getName()); attach.setOriginalName(bladeFile.getOriginalName()); attach.setAttachSize(fileSize); attach.setExtension(fileExtension); attachService.save(attach); return attach.getId(); } /** * 构建附件数据表 * * @param attachId 附件id * @param fileName 文件名 * @param fileSize 文件大小 * @param bladeFile 对象存储文件 * @return attachId */ private Long buildAttachData(Long attachId, String fileName, Long fileSize, BladeFile bladeFile, MultipartFile file) { try { String fileExtension = FileUtil.getFileExtension(fileName); AttachData attach = new AttachData(); attach.setAttachId(attachId); attach.setName(bladeFile.getName()); attach.setOriginalName(bladeFile.getOriginalName()); attach.setSize(fileSize); attach.setExtension(fileExtension); attach.setData(ImageUtils.mulToBase64(file)); attachDataService.save(attach); return attach.getId(); } catch (Exception e) { logger.error("附件数据表保存失败", e); } return 0L; } /** * 删除文件 * * @param fileName 存储桶对象名称 * @return R */ @SneakyThrows @PostMapping("/remove-file") @PreAuth(RoleConstant.HAS_ROLE_ADMIN) public R removeFile(@RequestParam String fileName) { ossBuilder.template().removeFile(fileName); return R.success("操作成功"); } /** * 批量删除文件 * * @param fileNames 存储桶对象名称集合 * @return R */ @SneakyThrows @PostMapping("/remove-files") @PreAuth(RoleConstant.HAS_ROLE_ADMIN) public R removeFiles(@RequestParam String fileNames) { ossBuilder.template().removeFiles(Func.toStrList(fileNames)); return R.success("操作成功"); } }