oying-system/src/main/resources/config/application-dev.yml
@@ -170,3 +170,9 @@ notify-url: https://localhost/oying/api/swiftPass/alipayCallback # 退款通知地址 refund-url: https://localhost/oying/api/swiftPass/returnNotify obs: access_key_id: RZ1UIOZDZ58DD4NWPD6Q access_key_secret: QpE58YEFtgoIwUoGNlN5JlNY7t6qVu7vMkix8gAI bucket: oying endpoint: https://obs.cn-southwest-2.myhuaweicloud.com oying-system/src/main/resources/config/application-prod.yml
@@ -180,3 +180,9 @@ notify-url: https://localhost/oying/api/swiftPass/alipayCallback # 退款通知地址 refund-url: https://localhost/oying/api/swiftPass/returnNotify obs: access_key_id: RZ1UIOZDZ58DD4NWPD6Q access_key_secret: QpE58YEFtgoIwUoGNlN5JlNY7t6qVu7vMkix8gAI bucket: oying endpoint: https://obs.cn-southwest-2.myhuaweicloud.com oying-tools/src/main/java/com/oying/domain/BucketStorage.java
New file @@ -0,0 +1,49 @@ package com.oying.domain; import com.oying.base.BaseEntity; import cn.hutool.core.bean.BeanUtil; import io.swagger.annotations.ApiModelProperty; import cn.hutool.core.bean.copier.CopyOptions; import java.io.Serializable; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Getter; import lombok.Setter; /** * @description / * @author lixin * @date 2025-06-03 **/ @Getter @Setter @TableName("tool_bucket_storage") public class BucketStorage extends BaseEntity implements Serializable { @TableId(value = "bucket_id", type = IdType.AUTO) @ApiModelProperty(value = "主键") private Long bucketId; @ApiModelProperty(value = "文件真实的名称") private String realName; @ApiModelProperty(value = "文件名") private String name; @ApiModelProperty(value = "后缀") private String suffix; @ApiModelProperty(value = "路径") private String path; @ApiModelProperty(value = "类型") private String type; @ApiModelProperty(value = "大小") private String size; public void copy(BucketStorage source){ BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true)); } } oying-tools/src/main/java/com/oying/domain/dto/BucketStorageQueryCriteria.java
New file @@ -0,0 +1,25 @@ package com.oying.domain.dto; import lombok.Data; import java.sql.Timestamp; import java.util.List; import io.swagger.annotations.ApiModelProperty; /** * @author lixin * @date 2025-06-03 **/ @Data public class BucketStorageQueryCriteria{ @ApiModelProperty(value = "页码", example = "1") private Integer page = 1; @ApiModelProperty(value = "每页数据量", example = "10") private Integer size = 10; @ApiModelProperty(value = "模糊查询") private String blurry; private List<Timestamp> createTime; } oying-tools/src/main/java/com/oying/mapper/BucketStorageMapper.java
New file @@ -0,0 +1,22 @@ package com.oying.mapper; import com.oying.domain.BucketStorage; import com.oying.domain.dto.BucketStorageQueryCriteria; import java.util.List; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; /** * @author lixin * @date 2025-06-03 **/ @Mapper public interface BucketStorageMapper extends BaseMapper<BucketStorage> { IPage<BucketStorage> findAll(@Param("criteria") BucketStorageQueryCriteria criteria, Page<Object> page); List<BucketStorage> findAll(@Param("criteria") BucketStorageQueryCriteria criteria); } oying-tools/src/main/java/com/oying/rest/BucketStorageController.java
New file @@ -0,0 +1,78 @@ package com.oying.rest; import com.oying.annotation.Log; import com.oying.domain.BucketStorage; import com.oying.exception.BadRequestException; import com.oying.service.BucketStorageService; import com.oying.domain.dto.BucketStorageQueryCriteria; import com.oying.utils.FileUtil; import lombok.RequiredArgsConstructor; import java.util.List; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import io.swagger.annotations.*; import java.io.IOException; import javax.servlet.http.HttpServletResponse; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.oying.utils.PageResult; import org.springframework.web.multipart.MultipartFile; /** * @author lixin * @date 2025-06-03 **/ @RestController @RequiredArgsConstructor @Api(tags = "存储桶") @RequestMapping("/api/bucketStorage") public class BucketStorageController { private final BucketStorageService bucketStorageService; @ApiOperation("导出数据") @GetMapping(value = "/download") @PreAuthorize("@el.check('bucketStorage:list')") public void exportBucketStorage(HttpServletResponse response, BucketStorageQueryCriteria criteria) throws IOException { bucketStorageService.download(bucketStorageService.queryAll(criteria), response); } @GetMapping @ApiOperation("查询存储桶") @PreAuthorize("@el.check('bucketStorage:list')") public ResponseEntity<PageResult<BucketStorage>> queryBucketStorage(BucketStorageQueryCriteria criteria){ Page<Object> page = new Page<>(criteria.getPage(), criteria.getSize()); return new ResponseEntity<>(bucketStorageService.queryAll(criteria,page),HttpStatus.OK); } @PostMapping @Log("上传存储桶文件") @ApiOperation("上传存储桶文件") @PreAuthorize("@el.check('bucketStorage:add')") public ResponseEntity<Object> create(@RequestParam String name, @RequestParam("file") MultipartFile file) { return new ResponseEntity<>(bucketStorageService.create(name, file), HttpStatus.CREATED); } @PostMapping("/pictures") @ApiOperation("上传图片") public ResponseEntity<Object> upload(@RequestParam MultipartFile file) { // 判断文件是否为图片 String suffix = FileUtil.getExtensionName(file.getOriginalFilename()); FileUtil.checkSize(5, file.getSize()); if (!FileUtil.IMAGE.equals(FileUtil.getFileType(suffix))) { throw new BadRequestException("只能上传图片"); } BucketStorage bucketStorage = bucketStorageService.create(null, file); return new ResponseEntity<>(bucketStorage, HttpStatus.OK); } @DeleteMapping @Log("删除存储桶") @ApiOperation("删除存储桶") @PreAuthorize("@el.check('bucketStorage:del')") public ResponseEntity<Object> deleteBucketStorage(@ApiParam(value = "传ID数组[]") @RequestBody List<Long> ids) { bucketStorageService.deleteAll(ids); return new ResponseEntity<>(HttpStatus.OK); } } oying-tools/src/main/java/com/oying/service/BucketStorageService.java
New file @@ -0,0 +1,57 @@ package com.oying.service; import com.oying.domain.BucketStorage; import com.oying.domain.dto.BucketStorageQueryCriteria; import java.io.File; import java.util.List; import java.io.IOException; import javax.servlet.http.HttpServletResponse; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import com.oying.utils.PageResult; import org.springframework.web.multipart.MultipartFile; /** * @description 服务接口 * @author lixin * @date 2025-06-03 **/ public interface BucketStorageService extends IService<BucketStorage> { /** * 查询数据分页 * @param criteria 条件 * @param page 分页参数 * @return PageResult */ PageResult<BucketStorage> queryAll(BucketStorageQueryCriteria criteria, Page<Object> page); /** * 查询所有数据不分页 * @param criteria 条件参数 * @return List<BucketStorageDto> */ List<BucketStorage> queryAll(BucketStorageQueryCriteria criteria); /** * 创建 */ BucketStorage create(String name, MultipartFile file); BucketStorage createFile(String name, File file); /** * 多选删除 * @param ids / */ void deleteAll(List<Long> ids); /** * 导出数据 * @param all 待导出的数据 * @param response / * @throws IOException / */ void download(List<BucketStorage> all, HttpServletResponse response) throws IOException; } oying-tools/src/main/java/com/oying/service/impl/BucketStorageServiceImpl.java
New file @@ -0,0 +1,133 @@ package com.oying.service.impl; import cn.hutool.core.util.ObjectUtil; import com.obs.services.internal.ObsProperties; import com.obs.services.model.AccessControlList; import com.obs.services.model.PutObjectResult; import com.oying.domain.BucketStorage; import com.oying.utils.FileUtil; import lombok.RequiredArgsConstructor; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.oying.service.BucketStorageService; import com.oying.domain.dto.BucketStorageQueryCriteria; import com.oying.mapper.BucketStorageMapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.oying.utils.PageUtil; import java.io.File; import java.util.List; import java.util.Map; import java.io.IOException; import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.LinkedHashMap; import com.oying.utils.PageResult; import org.springframework.web.multipart.MultipartFile; /** * @description 服务实现 * @author lixin * @date 2025-06-03 **/ @Service @RequiredArgsConstructor public class BucketStorageServiceImpl extends ServiceImpl<BucketStorageMapper, BucketStorage> implements BucketStorageService { private final BucketStorageMapper bucketStorageMapper; private final ObsProperties @Override public PageResult<BucketStorage> queryAll(BucketStorageQueryCriteria criteria, Page<Object> page){ return PageUtil.toPage(bucketStorageMapper.findAll(criteria, page)); } @Override public List<BucketStorage> queryAll(BucketStorageQueryCriteria criteria){ return bucketStorageMapper.findAll(criteria); } @Override @Transactional(rollbackFor = Exception.class) public BucketStorage createFile(String name, File file) { ObsConfig config = obsConfigService.findById(1L); FileUtil.checkSize(properties.getMaxSize(), file.length()); String suffix = FileUtil.getExtensionName(file.getName()); String type = FileUtil.getFileType(suffix); String reaName = System.currentTimeMillis() + "." + suffix; String objectKey = type + "/" + reaName; PutObjectResult result = ObsUtils.putObject(config, file, objectKey, AccessControlList.REST_CANNED_PUBLIC_READ); if (ObjectUtil.isNull(result)) { throw new BadRequestException("上传失败"); } name = StringUtils.isBlank(name) ? FileUtil.getFileNameNoEx(reaName) : name; BucketStorage bucketStorage = new BucketStorage( reaName, name, suffix, objectKey, type, FileUtil.getSize(file.length()) ); return bucketStorageRepository.save(bucketStorage); } @Override @Transactional(rollbackFor = Exception.class) public BucketStorage create(String name, MultipartFile file) { FileUtil.checkSize(properties.getMaxSize(), file.getSize()); String suffix = FileUtil.getExtensionName(file.getOriginalFilename()); String type = FileUtil.getFileType(suffix); String reaName = System.currentTimeMillis() + "." + suffix; String objectKey = type + "/" + reaName; PutObjectResult result = ObsUtils.putObject(config, FileUtil.toFile(file), objectKey,AccessControlList.REST_CANNED_PUBLIC_READ); if (ObjectUtil.isNull(result)) { throw new BadRequestException("上传失败"); } name = StringUtils.isBlank(name) ? FileUtil.getFileNameNoEx(reaName) : name; BucketStorage bucketStorage = new BucketStorage( reaName, name, suffix, objectKey, type, FileUtil.getSize(file.getSize()) ); return bucketStorageRepository.save(bucketStorage); } @Override @Transactional(rollbackFor = Exception.class) public void update(BucketStorage resources) { BucketStorage bucketStorage = getById(resources.getBucketId()); bucketStorage.copy(resources); bucketStorageMapper.updateById(bucketStorage); } @Override @Transactional(rollbackFor = Exception.class) public void deleteAll(List<Long> ids) { bucketStorageMapper.deleteBatchIds(ids); } @Override public void download(List<BucketStorage> all, HttpServletResponse response) throws IOException { List<Map<String, Object>> list = new ArrayList<>(); for (BucketStorage bucketStorage : all) { Map<String, Object> map = new LinkedHashMap<>(); map.put("文件真实的名称", bucketStorage.getRealName()); map.put("文件名", bucketStorage.getName()); map.put("后缀", bucketStorage.getSuffix()); map.put("路径", bucketStorage.getPath()); map.put("类型", bucketStorage.getType()); map.put("大小", bucketStorage.getSize()); map.put("创建人", bucketStorage.getCreateBy()); map.put("创建时间", bucketStorage.getCreateTime()); map.put("修改者", bucketStorage.getUpdateBy()); map.put("修改时间", bucketStorage.getUpdateTime()); list.add(map); } FileUtil.downloadExcel(list, response); } } oying-tools/src/main/java/com/oying/utils/ObsProperties.java
New file @@ -0,0 +1,21 @@ package com.oying.utils; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; /** * @author xin * @description * @date 2025/6/3 01:54 */ @Data @Configuration @ConfigurationProperties(prefix = "obs") public class ObsProperties { private String accessKeyId; private String accessKeySecret; private String bucket; private String endpoint; } oying-tools/src/main/java/com/oying/utils/ObsUtils.java
New file @@ -0,0 +1,127 @@ package com.oying.utils; import com.obs.services.ObsClient; import com.obs.services.model.AccessControlList; import com.obs.services.model.ObsObject; import com.obs.services.model.PutObjectRequest; import com.obs.services.model.PutObjectResult; import com.oying.exception.BadRequestException; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; public class ObsUtils { /** * 创建ObsClient实例 * * @param properties obs配置 * @return ObsClient实例 */ public static ObsClient getObsClient(ObsProperties properties) { // 创建ObsClient实例 return new ObsClient(properties.getAccessKeyId(), properties.getAccessKeySecret(), properties.getEndpoint()); } /** * 上传对象权限 * * @param properties obs配置 * @param file 文件 * @param objectKey 对象名 * @param acl 对象权限 * @return PutObjectResult */ public static PutObjectResult putObject(ObsProperties properties, File file, String objectKey, AccessControlList acl) { try { // 创建ObsClient实例 ObsClient obsClient = getObsClient(properties); PutObjectRequest request = new PutObjectRequest(); request.setBucketName(properties.getBucket()); request.setObjectKey(objectKey); request.setFile(file); // 对象权限 request.setAcl(acl); // 为待上传的本地文件路径,需要指定到具体的文件名 return obsClient.putObject(request); } catch (Exception e) { throw new BadRequestException("上传对象失败" + e.getMessage()); } finally { FileUtil.del(file); } } /** * 上传对象 * * @param properties obs配置 * @param file 文件 * @param objectKey 对象名 * @return PutObjectResult */ public static PutObjectResult putObject(ObsProperties properties, File file, String objectKey) { try { // 创建ObsClient实例 ObsClient obsClient = getObsClient(properties); // 为待上传的本地文件路径,需要指定到具体的文件名 return obsClient.putObject(properties.getBucket(), objectKey, file); } catch (Exception e) { throw new BadRequestException("上传对象失败" + e.getMessage()); } finally { FileUtil.del(file); } } /** * 下载对象 * * @param properties obs配置 * @param objectKey 对象名 * @param path 保存路径 */ public static String getObject(ObsProperties properties, String objectKey, String path) { // 创建ObsClient实例 ObsClient obsClient = getObsClient(properties); ObsObject obsObject = obsClient.getObject(properties.getBucket(), objectKey); InputStream in = obsObject.getObjectContent(); if (in != null) { FileOutputStream out = null; try { // getCanonicalFile 可解析正确各种路径 File dest = new File(path).getCanonicalFile(); // 检测是否存在目录 if (!dest.getParentFile().exists()) { dest.getParentFile().mkdirs(); } out = new FileOutputStream(path); byte[] b = new byte[1024]; int length; while ((length = in.read(b)) > 0) { out.write(b, 0, length); } } catch (Exception e) { throw new BadRequestException("流输出异常"); } finally { CloseUtil.close(in); CloseUtil.close(out); } } return path; } /** * 删除对象 * * @param properties obs配置 * @param objectKey 对象名 */ public static void deleteObject(ObsProperties properties, String objectKey) { // 创建ObsClient实例 ObsClient obsClient = getObsClient(properties); // 删除指定的对象 obsClient.deleteObject(properties.getBucket(), objectKey); } } pom.xml
@@ -236,6 +236,12 @@ <artifactId>commons-text</artifactId> <version>1.13.0</version> </dependency> <!-- 华为云对象存储服务 OBS--> <dependency> <groupId>com.huaweicloud</groupId> <artifactId>esdk-obs-java-bundle</artifactId> <version>3.22.12</version> </dependency> </dependencies> <build>