ref 脚手架同步为最新版本

This commit is contained in:
bootx
2024-10-04 21:55:03 +08:00
parent 8c2d860326
commit 380757016c
1516 changed files with 10762 additions and 65322 deletions

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.bootx.platform</groupId>
<artifactId>bootx-platform-starter</artifactId>
<version>3.0.0.beta1</version>
</parent>
<artifactId>starter-file</artifactId>
<description>文件存储</description>
<dependencies>
<!-- web容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--文件存储-->
<dependency>
<groupId>org.dromara.x-file-storage</groupId>
<artifactId>x-file-storage-spring</artifactId>
<version>${x-file-storage.version}</version>
</dependency>
<!-- 数据库 -->
<dependency>
<groupId>cn.bootx.platform</groupId>
<artifactId>common-mybatis-plus</artifactId>
<version>${bootx-platform.version}</version>
</dependency>
<!-- 认证 -->
<dependency>
<groupId>cn.bootx.platform</groupId>
<artifactId>starter-auth</artifactId>
<version>${bootx-platform.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,23 @@
package cn.bootx.platform.starter.file;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.x.file.storage.spring.EnableFileStorage;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.context.annotation.ComponentScan;
/**
* 文件管理
*
* @author xxm
* @since 2022/1/12
*/
@ComponentScan
@ConfigurationPropertiesScan
@EnableFileStorage
@AutoConfiguration
@MapperScan(annotationClass = Mapper.class)
public class FileAutoConfiguration {
}

View File

@@ -0,0 +1,33 @@
package cn.bootx.platform.starter.file.code;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* 存储平台类型
*
* @author xxm
* @since 2022/1/14
*/
@Getter
@RequiredArgsConstructor
public enum FilePlatformTypeEnum {
LOCAL("local"),
FTP("ftp"),
SFTP("sftp"),
WEB_DAV("web_dav"),
AMAZON("amazon"),
MINIO("minio"),
ALI("ali"),
HUAWEI("huawei"),
TENCENT("tencent"),
BAIDU("baidu"),
UPYUN("upyun"),
QINIU("qiniu"),
GOOGLE_CLOUD("google_cloud"),
FAST_DFS("fast_dfs"),
AZURE("azure");
private final String code;
}

View File

@@ -0,0 +1,30 @@
package cn.bootx.platform.starter.file.configuration;
import cn.hutool.core.util.StrUtil;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 文件上传配置
*
* @author xxm
* @since 2022/1/14
*/
@Data
@Accessors(chain = true)
@ConfigurationProperties(prefix = "bootx-platform.starter.file-upload")
public class FileUploadProperties {
/**
* 文件访问转发地址(当前后端服务地址或被代理后的地址), 流量会经过后端服务的转发
*/
private String forwardServerUrl = "http://127.0.0.1:9999";
/**
* 处理为 / 结尾
*/
public String getForwardServerUrl() {
return StrUtil.removeSuffix(forwardServerUrl, "/");
}
}

View File

@@ -0,0 +1,90 @@
package cn.bootx.platform.starter.file.controller;
import cn.bootx.platform.core.annotation.IgnoreAuth;
import cn.bootx.platform.core.rest.Res;
import cn.bootx.platform.core.rest.param.PageParam;
import cn.bootx.platform.core.rest.result.PageResult;
import cn.bootx.platform.core.rest.result.Result;
import cn.bootx.platform.starter.file.param.UploadFileQuery;
import cn.bootx.platform.starter.file.result.UploadFileResult;
import cn.bootx.platform.starter.file.service.FileUploadService;
import com.fhs.core.trans.anno.TransMethodResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
/**
* 文件上传
*
* @author xxm
* @since 2022/1/12
*/
@Tag(name = "文件上传")
@RestController
@RequestMapping("/file")
@RequiredArgsConstructor
public class FIleUpLoadController {
private final FileUploadService uploadService;
@IgnoreAuth
@TransMethodResult
@Operation(summary = "分页")
@GetMapping("/page")
public Result<PageResult<UploadFileResult>> page(PageParam pageParam, UploadFileQuery param) {
return Res.ok(uploadService.page(pageParam,param));
}
@TransMethodResult
@Operation(summary = "获取单条详情")
@GetMapping("/findById")
public Result<UploadFileResult> findById(Long id) {
return Res.ok(uploadService.findById(id));
}
@IgnoreAuth
@Operation(summary = "根据URL获取单条详情")
@GetMapping("/findByUrl")
public Result<UploadFileResult> findById(String url) {
return Res.ok(uploadService.findById(url));
}
@Operation(summary = "删除")
@PostMapping("/delete")
public Result<Void> delete(Long id) {
uploadService.delete(id);
return Res.ok();
}
@IgnoreAuth
@Operation(summary = "上传")
@PostMapping("/upload")
public Result<UploadFileResult> local(@RequestPart MultipartFile file, String fileName) {
return Res.ok(uploadService.upload(file, fileName));
}
@IgnoreAuth
@Operation(summary = "获取文件预览地址前缀(流量会经过后端)")
@GetMapping("/forward/getFilePreviewUrlPrefix")
public Result<String> getFilePreviewUrlPrefix() {
return Res.ok(uploadService.getServerFilePreviewUrlPrefix());
}
@IgnoreAuth
@Operation(summary = "预览文件(流量会经过后端)")
@GetMapping("/forward/preview/{id}")
public void preview(@PathVariable Long id, HttpServletResponse response) {
uploadService.preview(id, response);
}
@IgnoreAuth
@Operation(summary = "下载文件(流量会经过后端)")
@GetMapping("/forward/download/{id}")
public ResponseEntity<byte[]> download(@PathVariable Long id) {
return uploadService.download(id);
}
}

View File

@@ -0,0 +1,64 @@
package cn.bootx.platform.starter.file.controller;
import cn.bootx.platform.core.annotation.IgnoreAuth;
import cn.bootx.platform.core.annotation.RequestGroup;
import cn.bootx.platform.core.annotation.RequestPath;
import cn.bootx.platform.core.rest.Res;
import cn.bootx.platform.core.rest.result.Result;
import cn.bootx.platform.core.util.ValidationUtil;
import cn.bootx.platform.starter.file.param.FilePlatformParam;
import cn.bootx.platform.starter.file.result.FilePlatformResult;
import cn.bootx.platform.starter.file.service.FilePlatformService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 文件存储平台
* @author xxm
* @since 2024/8/12
*/
@Validated
@RequestGroup(groupCode = "file", groupName = "文件存储管理", moduleCode = "starter", moduleName = "starter模块")
@Tag(name = "文件存储平台")
@RestController
@RequestMapping("/file/platform")
@RequiredArgsConstructor
public class FilePlatformController {
private final FilePlatformService filePlatformService;
@IgnoreAuth
@Operation(summary = "列表")
@GetMapping("/findAll")
public Result<List<FilePlatformResult>> findAll(){
return Res.ok(filePlatformService.findAll());
}
@Operation(summary = "详情")
@GetMapping("/findById")
public Result<FilePlatformResult> findById(Long id){
return Res.ok(filePlatformService.findById(id));
}
@RequestPath("更新文件存储平台地址")
@Operation(summary = "更新文件存储平台地址")
@PostMapping("/updateUrl")
public Result<Void> update(@RequestBody FilePlatformParam filePlatform){
ValidationUtil.validateParam(filePlatform);
filePlatformService.updateUrl(filePlatform);
return Res.ok();
}
@RequestPath("设置默认存储平台地址")
@Operation(summary = "设置默认存储平台地址")
@PostMapping("/setDefault")
public Result<Void> setDefault(@NotNull(message = "主键不可为空") Long id){
filePlatformService.setDefault(id);
return Res.ok();
}
}

View File

@@ -0,0 +1,27 @@
package cn.bootx.platform.starter.file.convert;
import cn.bootx.platform.starter.file.entity.UploadFileInfo;
import cn.bootx.platform.starter.file.result.UploadFileResult;
import org.dromara.x.file.storage.core.FileInfo;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* @author xxm
* @since 2022/1/12
*/
@Mapper
public interface FileConvert {
FileConvert CONVERT = Mappers.getMapper(FileConvert.class);
UploadFileResult convert(UploadFileInfo in);
UploadFileInfo convert(FileInfo in);
FileInfo toFileInfo(UploadFileInfo in);
UploadFileResult toDto(FileInfo in);
}

View File

@@ -0,0 +1,19 @@
package cn.bootx.platform.starter.file.convert;
import cn.bootx.platform.starter.file.entity.FilePlatform;
import cn.bootx.platform.starter.file.result.FilePlatformResult;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
*
* @author xxm
* @since 2024/8/12
*/
@Mapper
public interface FilePlatformConvert {
FilePlatformConvert CONVERT = Mappers.getMapper(FilePlatformConvert.class);
FilePlatformResult toResult(FilePlatform filePlatform);
}

View File

@@ -0,0 +1,18 @@
package cn.bootx.platform.starter.file.dao;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.starter.file.entity.FilePlatform;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
/**
*
* @author xxm
* @since 2024/8/12
*/
@Slf4j
@Repository
@RequiredArgsConstructor
public class FilePlatformManager extends BaseManager<FilePlatformMapper, FilePlatform> {
}

View File

@@ -0,0 +1,14 @@
package cn.bootx.platform.starter.file.dao;
import cn.bootx.platform.starter.file.entity.FilePlatform;
import com.github.yulichang.base.MPJBaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
*
* @author xxm
* @since 2024/8/12
*/
@Mapper
public interface FilePlatformMapper extends MPJBaseMapper<FilePlatform> {
}

View File

@@ -0,0 +1,55 @@
package cn.bootx.platform.starter.file.dao;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.core.rest.param.PageParam;
import cn.bootx.platform.starter.file.entity.UploadFileInfo;
import cn.bootx.platform.starter.file.param.UploadFileQuery;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import java.util.Objects;
import java.util.Optional;
/**
* @author xxm
* @since 2022/1/12
*/
@Slf4j
@Repository
@RequiredArgsConstructor
public class UploadFileManager extends BaseManager<UploadFileMapper, UploadFileInfo> {
/**
* 根据URL查询
*/
public Optional<UploadFileInfo> findByUrl(String url){
return findByField(UploadFileInfo::getUrl, url);
}
/**
* 根据URL删除
*/
public boolean deleteByUrl(String url){
return deleteByField(UploadFileInfo::getUrl, url);
}
/**
* 分页
*/
public Page<UploadFileInfo> page(PageParam pageParam, UploadFileQuery param) {
Page<UploadFileInfo> mpPage = MpUtil.getMpPage(pageParam);
return lambdaQuery()
.like(StrUtil.isNotBlank(param.getOriginalFilename()), UploadFileInfo::getOriginalFilename, param.getOriginalFilename())
.like(StrUtil.isNotBlank(param.getExt()), UploadFileInfo::getExt, param.getExt())
.like(StrUtil.isNotBlank(param.getContentType()), UploadFileInfo::getContentType, param.getContentType())
.ge(Objects.nonNull(param.getStartTime()), UploadFileInfo::getCreateTime, param.getStartTime())
.le(Objects.nonNull(param.getEndTime()), UploadFileInfo::getCreateTime, param.getEndTime())
.orderByDesc(UploadFileInfo::getId).page(mpPage);
}
}

View File

@@ -0,0 +1,14 @@
package cn.bootx.platform.starter.file.dao;
import cn.bootx.platform.starter.file.entity.UploadFileInfo;
import com.github.yulichang.base.MPJBaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* @author xxm
* @since 2022/1/12
*/
@Mapper
public interface UploadFileMapper extends MPJBaseMapper<UploadFileInfo> {
}

View File

@@ -0,0 +1,57 @@
package cn.bootx.platform.starter.file.entity;
import cn.bootx.platform.common.mybatisplus.base.MpRealDelEntity;
import cn.bootx.platform.common.mybatisplus.function.ToResult;
import cn.bootx.platform.starter.file.code.FilePlatformTypeEnum;
import cn.bootx.platform.starter.file.convert.FilePlatformConvert;
import cn.bootx.platform.starter.file.result.FilePlatformResult;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import lombok.experimental.FieldNameConstants;
/**
* 文件存储平台
* @author xxm
* @since 2024/8/12
*/
@EqualsAndHashCode(callSuper = true)
@Data
@FieldNameConstants
@Accessors(chain = true)
@TableName("starter_file_platform")
public class FilePlatform extends MpRealDelEntity implements ToResult<FilePlatformResult> {
/**
* 接入类型
* @see FilePlatformTypeEnum
*/
@TableField(updateStrategy = FieldStrategy.NEVER)
private String type;
/** 名称 */
@TableField(updateStrategy = FieldStrategy.NEVER)
private String name;
/** 默认存储平台 */
private boolean defaultPlatform;
/** 访问地址 */
private String url;
public String getUrl() {
return StrUtil.removeSuffix(url, "/");
}
/**
* 转换
*/
@Override
public FilePlatformResult toResult() {
return FilePlatformConvert.CONVERT.toResult(this);
}
}

View File

@@ -0,0 +1,127 @@
package cn.bootx.platform.starter.file.entity;
import cn.bootx.platform.common.mybatisplus.base.MpIdEntity;
import cn.bootx.platform.common.mybatisplus.function.ToResult;
import cn.bootx.platform.starter.file.convert.FileConvert;
import cn.bootx.platform.starter.file.result.UploadFileResult;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.dromara.x.file.storage.core.FileInfo;
import java.time.LocalDateTime;
/**
* 上传文件信息
*
* @author xxm
* @since 2022/1/12
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@TableName(value = "starter_file_upload_info", autoResultMap = true)
public class UploadFileInfo extends MpIdEntity implements ToResult<UploadFileResult> {
/**
* 文件访问地址
*/
private String url;
/**
* 文件大小,单位字节
*/
private Long size;
/**
* 文件名称
*/
private String filename;
/**
* 原始文件名
*/
private String originalFilename;
/**
* 基础存储路径
*/
private String basePath;
/**
* 存储路径
*/
private String path;
/**
* 文件扩展名
*/
private String ext;
/**
* MIME 类型
*/
private String contentType;
/**
* 存储平台
*/
private String platform;
/**
* 缩略图访问路径
*/
private String thUrl;
/**
* 缩略图名称
*/
private String thFilename;
/**
* 缩略图大小,单位字节
*/
private Long thSize;
/**
* 缩略图 MIME 类型
*/
private String thContentType;
/**
* 文件所属对象id
*/
private String objectId;
/**
* 文件所属对象类型,例如用户头像,评价图片
*/
private String objectType;
/** 创建时间 */
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@Override
public UploadFileResult toResult() {
return FileConvert.CONVERT.convert(this);
}
/**
* 初始化创建
*/
public static UploadFileInfo init(FileInfo fileInfo){
return FileConvert.CONVERT.convert(fileInfo);
}
/**
* 转换为 x.file.storage 的文件信息对象
*/
public FileInfo toFileInfo(){
return FileConvert.CONVERT.toFileInfo(this);
}
}

View File

@@ -0,0 +1,51 @@
package cn.bootx.platform.starter.file.handler;
import cn.bootx.platform.starter.file.dao.UploadFileManager;
import cn.bootx.platform.starter.file.entity.UploadFileInfo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.x.file.storage.core.FileInfo;
import org.dromara.x.file.storage.core.recorder.DefaultFileRecorder;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
/**
* x.file.storage 文件上传信息储存
* @author xxm
* @since 2023/11/13
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class FileDetailRecordHandler extends DefaultFileRecorder {
private final UploadFileManager uploadFileManager;
/**
* 保存文件记录
*/
@Override
public boolean save(FileInfo fileInfo) {
UploadFileInfo uploadFileInfo = UploadFileInfo.init(fileInfo);
uploadFileInfo.setCreateTime(LocalDateTime.now());
uploadFileManager.save(uploadFileInfo);
fileInfo.setId(String.valueOf(uploadFileInfo.getId()));
return true;
}
/**
* 根据URL获取文件记录
*/
@Override
public FileInfo getByUrl(String url) {
return uploadFileManager.findByUrl(url).map(UploadFileInfo::toFileInfo).orElse(null);
}
/**
* 根据URL删除
*/
@Override
public boolean delete(String url) {
return uploadFileManager.deleteByUrl(url);
}
}

View File

@@ -0,0 +1,27 @@
package cn.bootx.platform.starter.file.param;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 存储平台配置参数
* @author xxm
* @since 2024/8/12
*/
@Data
@Accessors(chain = true)
@Schema(title = "存储平台配置参数")
public class FilePlatformParam {
@NotNull(message = "主键不得为空")
@Schema(description = "主键")
private Long id;
@NotBlank(message = "平台地址不得为空")
@Schema(description = "平台地址")
private String url;
}

View File

@@ -0,0 +1,19 @@
package cn.bootx.platform.starter.file.param;
import cn.bootx.platform.common.mybatisplus.query.entity.SortParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 存储平台查询参数
* @author xxm
* @since 2024/8/12
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "存储平台查询参数")
public class FilePlatformQuery extends SortParam {
}

View File

@@ -0,0 +1,39 @@
package cn.bootx.platform.starter.file.param;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 上传文件查询参数
* @author xxm
* @since 2023/11/15
*/
@Data
@Accessors(chain = true)
@Schema(title = "上传文件查询参数")
public class UploadFileQuery {
/** 原始文件名 */
@Schema(description = "原始文件名")
private String originalFilename;
/** 开始时间 */
@Schema(description = "开始时间")
private LocalDateTime startTime;
/** 结束时间 */
@Schema(description = "结束时间")
private LocalDateTime endTime;
/** 文件扩展名 */
@Schema(description = "文件扩展名")
private String ext;
/** MIME 类型 */
@Schema(description = "MIME 类型")
private String contentType;
}

View File

@@ -0,0 +1,39 @@
package cn.bootx.platform.starter.file.result;
import cn.bootx.platform.core.result.BaseResult;
import cn.bootx.platform.starter.file.code.FilePlatformTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 文件存储平台
* @author xxm
* @since 2024/8/12
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "文件存储平台")
public class FilePlatformResult extends BaseResult {
/**
* 平台类型
* @see FilePlatformTypeEnum
*/
@Schema(description = "平台类型")
private String type;
/** 名称 */
@Schema(description = "名称")
private String name;
/** 默认存储平台 */
@Schema(description = "默认存储平台")
private Boolean defaultPlatform;
/** 访问地址 */
@Schema(description = "访问地址")
private String url;
}

View File

@@ -0,0 +1,182 @@
package cn.bootx.platform.starter.file.result;
import cn.bootx.platform.core.result.BaseResult;
import cn.bootx.platform.starter.file.entity.FilePlatform;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Dict;
import com.fhs.core.trans.anno.Trans;
import com.fhs.core.trans.constant.TransType;
import com.fhs.core.trans.vo.TransPojo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import lombok.experimental.FieldNameConstants;
import org.dromara.x.file.storage.core.FileInfo;
import org.dromara.x.file.storage.core.constant.Constant;
import java.util.Map;
/**
* 上传文件信息
*
* @author xxm
* @since 2022/1/12
*/
@FieldNameConstants
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "上传文件信息")
public class UploadFileResult extends BaseResult implements TransPojo {
/**
* 文件访问地址
*/
@Schema(description = "访问地址")
private String url;
/**
* 文件大小,单位字节
*/
@Schema(description = "文件大小")
private Long size;
/**
* 文件名称
*/
@Schema(description = "文件名称")
private String filename;
/**
* 原始文件名
*/
@Schema(description = "原始文件名")
private String originalFilename;
/**
* 基础存储路径
*/
@Schema(description = "基础存储路径")
private String basePath;
/**
* 存储路径
*/
@Schema(description = "存储路径")
private String path;
/**
* 文件扩展名
*/
@Schema(description = "扩展名")
private String ext;
/**
* MIME 类型
*/
@Schema(description = "MIME 类型")
private String contentType;
/**
* 存储平台
*/
@Trans(type = TransType.SIMPLE, target = FilePlatform.class, fields = FilePlatform.Fields.name, ref = Fields.platformName, uniqueField=FilePlatform.Fields.type)
@Schema(description = "存储平台")
private String platform;
/**
* 存储平台
*/
private String platformName;
/**
* 缩略图访问路径
*/
private String thUrl;
/**
* 缩略图名称
*/
private String thFilename;
/**
* 缩略图大小,单位字节
*/
private Long thSize;
/**
* 缩略图 MIME 类型
*/
private String thContentType;
/**
* 文件所属对象id
*/
private String objectId;
/**
* 文件所属对象类型,例如用户头像,评价图片
*/
private String objectType;
/**
* 文件元数据
*/
private Map<String, String> metadata;
/**
* 文件用户元数据
*/
private Map<String, String> userMetadata;
/**
* 缩略图元数据
*/
private Map<String, String> thMetadata;
/**
* 缩略图用户元数据
*/
private Map<String, String> thUserMetadata;
/**
* 附加属性字典
*/
private Dict attr;
/**
* 文件的访问控制列表,一般情况下只有对象存储支持该功能,支持 String 或对应存储平台的 ACL 对象
* <pre class="code">
* //方式一,通过字符串设置通用的 ACL 详情:{@link Constant.ACL }
* setFileAcl(ACL.PUBLIC_READ);
* //方式二,针对指定存储平台设置更复杂的权限控制,以华为云 OBS 为例
* AccessControlList acl = new AccessControlList();
* Owner owner = new Owner();
* owner.setId("ownerid");
* acl.setOwner(owner);
* // 保留Owner的完全控制权限如果不设置该权限该对象Owner自身将没有访问权限
* acl.grantPermission(new CanonicalGrantee("ownerid"), Permission.PERMISSION_FULL_CONTROL);
* // 为指定用户设置完全控制权限
* acl.grantPermission(new CanonicalGrantee("userid"), Permission.PERMISSION_FULL_CONTROL);
* // 为所有用户设置读权限
* acl.grantPermission(GroupGrantee.ALL_USERS, Permission.PERMISSION_READ);
* setFileAcl(acl);
* <pre/>
*/
private Object fileAcl;
/**
* 缩略图的访问控制列表,一般情况下只有对象存储支持该功能
* 详情见{@link FileInfo#setFileAcl}
*/
private Object thFileAcl;
/**
* 文件大小
*/
public String getFileSize() {
return FileUtil.readableFileSize(size);
}
}

View File

@@ -0,0 +1,73 @@
package cn.bootx.platform.starter.file.service;
import cn.bootx.platform.common.mybatisplus.base.MpRealDelEntity;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.core.exception.DataNotExistException;
import cn.bootx.platform.starter.auth.util.SecurityUtil;
import cn.bootx.platform.starter.file.dao.FilePlatformManager;
import cn.bootx.platform.starter.file.entity.FilePlatform;
import cn.bootx.platform.starter.file.param.FilePlatformParam;
import cn.bootx.platform.starter.file.result.FilePlatformResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
/**
* 存储平台
* @author xxm
* @since 2024/8/12
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class FilePlatformService {
private final FilePlatformManager filePlatformManager;
/**
* 获取全部存储平台
*/
public List<FilePlatformResult> findAll(){
return MpUtil.toListResult(filePlatformManager.findAll());
}
/**
* 获取存储平台
*/
public FilePlatformResult findById(Long id){
return filePlatformManager.findById(id).map(FilePlatform::toResult).orElseThrow(() -> new DataNotExistException("存储平台不存在"));
}
/**
* 更新
*/
public void updateUrl(FilePlatformParam filePlatform){
FilePlatform platform = filePlatformManager.findById(filePlatform.getId())
.orElseThrow(() -> new DataNotExistException("存储平台不存在"));
platform.setUrl(filePlatform.getUrl());
filePlatformManager.updateById(platform);
}
/**
* 设为默认
*/
@Transactional(rollbackFor = Exception.class)
public void setDefault(Long id){
filePlatformManager.lambdaUpdate()
.set(FilePlatform::isDefaultPlatform, false)
.eq(FilePlatform::isDefaultPlatform, true)
.set(FilePlatform::getLastModifiedTime, LocalDateTime.now())
.set(MpRealDelEntity::getLastModifier, SecurityUtil.getUserIdOrDefaultId())
.update();
filePlatformManager.lambdaUpdate()
.eq(FilePlatform::getId, id)
.set(FilePlatform::getLastModifiedTime, LocalDateTime.now())
.set(MpRealDelEntity::getLastModifier, SecurityUtil.getUserIdOrDefaultId())
.set(FilePlatform::isDefaultPlatform, true)
.update();
}
}

View File

@@ -0,0 +1,153 @@
package cn.bootx.platform.starter.file.service;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.core.exception.DataNotExistException;
import cn.bootx.platform.core.rest.param.PageParam;
import cn.bootx.platform.core.rest.result.PageResult;
import cn.bootx.platform.starter.file.configuration.FileUploadProperties;
import cn.bootx.platform.starter.file.convert.FileConvert;
import cn.bootx.platform.starter.file.dao.UploadFileManager;
import cn.bootx.platform.starter.file.entity.UploadFileInfo;
import cn.bootx.platform.starter.file.param.UploadFileQuery;
import cn.bootx.platform.starter.file.result.UploadFileResult;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.dromara.x.file.storage.core.FileInfo;
import org.dromara.x.file.storage.core.FileStorageService;
import org.dromara.x.file.storage.core.upload.UploadPretreatment;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayInputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
/**
* 文件上传管理类
*
* @author xxm
* @since 2022/1/14
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class FileUploadService {
private final UploadFileManager uploadFileManager;
private final FileStorageService fileStorageService;
private final FileUploadProperties fileUploadProperties;
/**
* 分页
*/
public PageResult<UploadFileResult> page(PageParam pageParam, UploadFileQuery param) {
return MpUtil.toPageResult(uploadFileManager.page(pageParam,param));
}
/**
* 获取单条详情
*/
public UploadFileResult findById(Long id){
return uploadFileManager.findById(id)
.map(UploadFileInfo::toResult)
.orElseThrow(DataNotExistException::new);
}
/**
* 根据URL获取单条详情
*/
public UploadFileResult findById(String url){
return uploadFileManager.findByUrl(url)
.map(UploadFileInfo::toResult)
.orElseThrow(DataNotExistException::new);
}
/**
* 文件删除
*/
@Transactional(rollbackFor = Exception.class)
public void delete(Long id){
UploadFileInfo uploadFileInfo = uploadFileManager.findById(id)
.orElseThrow(DataNotExistException::new);
fileStorageService.delete(FileConvert.CONVERT.toFileInfo(uploadFileInfo));
}
/**
* 文件上传
* @param file 文件
* @param fileName 文件名称
*/
@Transactional(rollbackFor = Exception.class)
public UploadFileResult upload(MultipartFile file, String fileName) {
UploadPretreatment uploadPretreatment = fileStorageService.of(file);
if (StrUtil.isNotBlank(fileName)){
uploadPretreatment.setOriginalFilename(fileName);
}
// 按年月日进行分目录
uploadPretreatment.setPath(LocalDateTimeUtil.format(LocalDateTime.now(), "yyyy/MM/dd/"));
FileInfo upload = uploadPretreatment.upload();
return FileConvert.CONVERT.toDto(upload);
}
/**
* 文件预览
*/
@SneakyThrows
public void preview(Long id, HttpServletResponse response) {
FileInfo info = fileStorageService.getFileInfoByUrl(String.valueOf(id));
if (info == null){
log.warn("文件不存在");
return;
}
byte[] bytes = fileStorageService.download(info).bytes();
var is = new ByteArrayInputStream(bytes);
// 获取响应输出流
ServletOutputStream os = response.getOutputStream();
IoUtil.copy(is, os);
response.addHeader(HttpHeaders.CONTENT_DISPOSITION, info.getContentType());
IoUtil.close(is);
IoUtil.close(os);
}
/**
* 文件下载
*/
@SneakyThrows
public ResponseEntity<byte[]> download(Long id) {
FileInfo fileInfo = fileStorageService.getFileInfoByUrl(String.valueOf(id));
byte[] bytes = fileStorageService.download(fileInfo).bytes();
// 设置header信息
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
String fileName = fileInfo.getOriginalFilename();
headers.setContentDispositionFormData("attachment", URLEncoder.encode(fileName, StandardCharsets.UTF_8));
return new ResponseEntity<>(bytes,headers,HttpStatus.OK);
}
/**
* 文件访问转发地址(当前后端服务地址或被代理后的地址), 流量会经过后端服务的转发
*/
public String getServerFilePreviewUrlPrefix() {
return this.getForwardServerUrl() + "/file/preview/";
}
/**
* 文件访问转发地址(当前后端服务地址或被代理后的地址), 流量会经过后端服务的转发
*/
private String getForwardServerUrl() {
return fileUploadProperties.getForwardServerUrl();
}
}