first commit

master
chenjf 2 years ago
parent c91a7a7919
commit 3ee856a360

@ -0,0 +1,13 @@
package org.ils.file;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
System.out.println( "Hello World!" );
}
}

@ -0,0 +1,18 @@
package org.ils.file;
import cn.xuyanwu.spring.file.storage.spring.EnableFileStorage;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author chenjf
* @datetime 2023/08/07
*/
@EnableFileStorage
@SpringBootApplication
public class IlsFileStorageTestApplication {
public static void main(String[] args) {
SpringApplication.run(IlsFileStorageTestApplication.class,args);
}
}

@ -0,0 +1,34 @@
package org.ils.file.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import java.util.Collections;
@EnableTransactionManagement
@Configuration
@MapperScan("org.ils.file.mapper")
public class MyBatisPlusConfig {
@Bean
public PaginationInnerInterceptor paginationInnerInterceptor() {
PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor();
// 设置最大单页限制数量,默认 500 条,-1 不受限制
paginationInterceptor.setMaxLimit(-1L);
paginationInterceptor.setDbType(DbType.MYSQL);
// 开启 count 的 join 优化,只针对部分 left join
paginationInterceptor.setOptimizeJoin(true);
return paginationInterceptor;
}
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.setInterceptors(Collections.singletonList(paginationInnerInterceptor()));
return mybatisPlusInterceptor;
}
}

@ -0,0 +1,16 @@
package org.ils.file.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* @author chenjf
*/
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

@ -0,0 +1,70 @@
package org.ils.file.config;
import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author chenjf
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {
//配置了 Swagger 的Docket的bean实例
@Bean
public Docket docket(){
ParameterBuilder ticketPar = new ParameterBuilder();
List<Parameter> pars = new ArrayList<>();
ticketPar.name("Authorization").description("token")
.modelRef(new ModelRef("string")).parameterType("header")
.required(false).build();
pars.add(ticketPar.build());
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
//RequestHandlerSelectors, 配置要扫描接口的方式
//basePackage:指定要扫描的包
//any():扫描全部
//withClassAnnotation: 扫描类上的注解
//withMethodAnnotation: 扫描方法上的注解
.apis(RequestHandlerSelectors.basePackage("org.ils.file"))
.apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build()
.globalOperationParameters(pars);
}
//配置Swagger 信息=apiInfo
private ApiInfo apiInfo(){
return new ApiInfo(
"ils fileStorage Api",
"ils文件上传下载接口",
"v1.0",
"127.0.0.1:9098/",
null,//contact
"Apache 2.0",
"http://www.apache.org/licenses/LICENSE-2.0",
new ArrayList()
);
}
}

@ -0,0 +1,24 @@
package org.ils.file.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author chenjf
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
registry.addResourceHandler("doc.html")
.addResourceLocations("classpath:/META-INF/resources/");
}
}

@ -0,0 +1,82 @@
package org.ils.file.controller;
import cn.xuyanwu.spring.file.storage.FileInfo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.ils.file.service.impl.IlsFileDetailServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
* @author chenjf
* @datetime 2023/08/07
*/
@RestController
@RequestMapping("/api/v1/ils/file/")
@Slf4j
@Api(value = "ils-文件上传下载接口", tags = {"ils文件上传下载接口"})
public class FileDetailController {
@Autowired
private IlsFileDetailServiceImpl ilsFileDetailService;
/**
*
*/
@ApiOperation("上传文件-返回fileInfo")
@RequestMapping(value = "upload", method = RequestMethod.POST)
public FileInfo upload(MultipartFile file) {
return ilsFileDetailService.upload(file);
}
/**
* url
*/
@ApiOperation("上传文件-返回url")
@RequestMapping(value = "uploadUrl", method = RequestMethod.POST)
public String uploadUrl(MultipartFile file) {
return ilsFileDetailService.uploadUrl(file);
}
/**
*
* @param file
* @return
*/
@ApiOperation("上传图片-缩略图")
@RequestMapping(value = "uploadImage", method = RequestMethod.POST)
public FileInfo uploadImage(MultipartFile file) {
return ilsFileDetailService.uploadImage(file);
}
/**
*
*/
@ApiOperation("上传文件到指定阿里云存储平台,成功返回文件信息")
@RequestMapping(value = "uploadPlatform", method = RequestMethod.POST)
public FileInfo uploadPlatform(MultipartFile file, String platform) {
return ilsFileDetailService.uploadPlatform(file, platform);
}
/**
* url
*/
@ApiOperation("根据url下载文件流")
@RequestMapping(value = "downloadByUrl", method = RequestMethod.POST)
public void downloadByUrl(String url, HttpServletResponse response) throws IOException {
ilsFileDetailService.downloadByUrl(url, response);
}
}

@ -0,0 +1,82 @@
package org.ils.file.entity;
import cn.hutool.core.date.DateTime;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
/**
* @author chenjf
* @datatime 2023/08/07
*/
@Data
@NoArgsConstructor
public class FileDetail implements Serializable {
@ApiModelProperty(value = "主键id", name = "id" )
private String id;
@ApiModelProperty(value = "文件访问地址", name = "url " )
private String url;
@ApiModelProperty(value = "文件大小,单位字节", name = "size" )
private Long size;
@ApiModelProperty(value = "文件名称", name = "filename" )
private String filename;
@ApiModelProperty(value = "原始文件名", name = "originalFilename" )
private String originalFilename;
@ApiModelProperty(value = "基础存储路径", name = "basePath" )
private String basePath;
@ApiModelProperty(value = "存储路径", name = "path" )
private String path;
@ApiModelProperty(value = "文件扩展名", name = "ext" )
private String ext;
@ApiModelProperty(value = "MIME类型", name = "contentType" )
private String contentType;
@ApiModelProperty(value = "存储平台", name = "platform" )
private String platform;
@ApiModelProperty(value = "缩略图访问路径", name = "thUrl" )
private String thUrl;
@ApiModelProperty(value = "缩略图名称", name = "thFilename" )
private String thFilename;
@ApiModelProperty(value = "缩略图大小,单位字节", name = "thSize" )
private Long thSize;
@ApiModelProperty(value = "缩略图MIME类型", name = "thContentType" )
private String thContentType;
@ApiModelProperty(value = "文件所属对象id", name = "objectId" )
private String objectId;
@ApiModelProperty(value = "文件所属对象类型,例如用户头像,评价图片", name = "objectType" )
private String objectType;
@ApiModelProperty(value = "附加属性", name = "attr" )
private String attr;
@ApiModelProperty(value = "文件ACL", name = "fileAcl" )
private String fileAcl;
@ApiModelProperty(value = "缩略图文件ACL", name = "thFileAcl" )
private String thFileAcl;
@ApiModelProperty(value = "创建时间", name = "createTime" )
private Date createTime;
}

@ -0,0 +1,9 @@
package org.ils.file.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.ils.file.entity.FileDetail;
@Mapper
public interface FileDetailMapper extends BaseMapper<FileDetail> {
}

@ -0,0 +1,40 @@
package org.ils.file.service;
import cn.xuyanwu.spring.file.storage.FileInfo;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author chenjf
* @datetime 2023/08/07
*/
public interface IlsFileDetailService {
/**
*
*/
public FileInfo upload(MultipartFile file);
/**
* url
*/
public String uploadUrl(MultipartFile file);
/**
*
*/
public FileInfo uploadImage(MultipartFile file);
/**
*
*/
public FileInfo uploadPlatform(MultipartFile file, String platform);
/**
* url
* @param url
*/
public void downloadByUrl(String url, HttpServletResponse response) throws IOException;
}

@ -0,0 +1,66 @@
package org.ils.file.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import cn.xuyanwu.spring.file.storage.FileInfo;
import cn.xuyanwu.spring.file.storage.recorder.FileRecorder;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.ils.file.entity.FileDetail;
import org.ils.file.mapper.FileDetailMapper;
import org.springframework.stereotype.Service;
/**
* service
* @author chenjf
*/
@Slf4j
@Service
public class FileDetailService extends ServiceImpl<FileDetailMapper, FileDetail> implements FileRecorder {
@SneakyThrows
@Override
public boolean save(FileInfo info){
FileDetail detail = BeanUtil.copyProperties(info, FileDetail.class,"attr");
//这是手动获 取附加属性字典 并转成 json 字符串,方便存储在数据库中
if (info.getAttr() != null) {
detail.setAttr(new ObjectMapper().writeValueAsString(info.getAttr()));
}
boolean b = save(detail);
if (b) {
info.setId(detail.getId());
}
return b;
}
/**
* url
*/
@SneakyThrows
@Override
public FileInfo getByUrl(String url) {
FileDetail detail = getOne(new QueryWrapper<FileDetail>().eq("url", url));
FileInfo info = BeanUtil.copyProperties(detail, FileInfo.class,"attr");
//这是手动获取数据库中的 json 字符串 并转成 附加属性字典,方便使用
if (StrUtil.isNotBlank(detail.getAttr())) {
info.setAttr(new ObjectMapper().readValue(detail.getAttr(), Dict.class));
}
return info;
}
/**
* url
*/
@Override
public boolean delete(String url) {
return remove(new QueryWrapper<FileDetail>().eq("url", url));
}
}

@ -0,0 +1,89 @@
package org.ils.file.service.impl;
import cn.xuyanwu.spring.file.storage.Downloader;
import cn.xuyanwu.spring.file.storage.FileInfo;
import cn.xuyanwu.spring.file.storage.FileStorageService;
import lombok.extern.slf4j.Slf4j;
import org.ils.file.service.IlsFileDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* @author chenjf
* @datetime 2023/08/07
*/
@Slf4j
@Service
public class IlsFileDetailServiceImpl implements IlsFileDetailService {
@Autowired
private FileStorageService fileStorageService;
@Autowired
private FileDetailService fileDetailService;
@Override
public FileInfo upload(MultipartFile file) {
FileInfo fileInfo = fileStorageService.of(file).upload();
return fileInfo;
}
@Override
public String uploadUrl(MultipartFile file) {
FileInfo fileInfo = fileStorageService.of(file)
.setPath("upload/") //保存到相对路径下,为了方便管理,不需要可以不写
.setObjectId("0") //关联对象id为了方便管理不需要可以不写
.setObjectType("0") //关联对象类型,为了方便管理,不需要可以不写
.putAttr("role","admin") //保存一些属性,可以在切面、保存上传记录、自定义存储平台等地方获取使用,不需要可以不写
.upload(); //将文件上传到对应地方
return fileInfo == null ? "上传失败!" : fileInfo.getUrl();
}
@Override
public FileInfo uploadImage(MultipartFile file) {
FileInfo fileInfo = fileStorageService.of(file)
.image(img -> img.size(1000,1000)) //将图片大小调整到 1000*1000
.thumbnail(th -> th.size(200,200)) //再生成一张 200*200 的缩略图
.upload();
return fileInfo;
}
@Override
public FileInfo uploadPlatform(MultipartFile file, String platform) {
FileInfo fileInfo = fileStorageService.of(file)
.setPlatform(platform) //使用指定的存储平台
.upload();
return fileInfo;
}
@Override
public void downloadByUrl(String url,HttpServletResponse response) throws IOException {
FileInfo fileInfo = fileStorageService.getFileInfoByUrl(url);
byte[] bytes = fileStorageService.download(fileInfo).bytes();
// 清空response
response.reset();
// 设置response的Header
response.addHeader("Content-Disposition", "attachment;filename=" + new String(String.valueOf(fileInfo.getFilename())));
response.addHeader("Content-Length", "" + fileInfo.getSize());
response.setContentType("application/octet-stream");
OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
toClient.write(bytes);
toClient.flush();
toClient.close();
}
}

@ -0,0 +1,97 @@
server:
port: 9098
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB
datasource:
url: jdbc:mysql://127.0.0.1:3306/ils_fileStorage?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&AllowPublicKeyRetrieval=True&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
druid: # 全局druid参数绝大部分值和默认保持一致。(现已支持的参数如下,不清楚含义不要乱设置)
# 连接池的配置信息
# 初始化大小,最小,最大
initial-size: 50
min-idle: 5
maxActive: 200
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 打开PSCache并且指定每个连接上PSCache的大小
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
# 配置监控统计拦截的filters去掉后监控界面sql无法统计'wall'用于防火墙
#filters: stat,wall,slf4j
filters: stat,slf4j
# 通过connectProperties属性来打开mergeSql功能慢SQL记录
# connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
file-storage: #文件存储配置
default-platform: aliyun-oss-1 #默认使用的存储平台
thumbnail-suffix: ".min.jpg" #缩略图后缀,例如【.min.jpg】【.png】
aliyun-oss:
- platform: aliyun-oss-1 # 存储平台标识
enable-storage: true # 启用存储
access-key: LTAI5tP5t7QzS1moNkgvwQ4r
secret-key: TmZIsC9KMQEaVsBOLGYdkIkIodTxBj
end-point: oss-cn-shenzhen.aliyuncs.com
bucket-name: ilsgoods
domain: https://ilsgoods.oss-cn-shenzhen.aliyuncs.com/ # 访问域名,注意“/”结尾例如https://abc.oss-cn-shanghai.aliyuncs.com/
base-path: upload/test/
minio:
- platform: minio-1 # 存储平台标识
enable-storage: true # 启用存储
access-key: admin
secret-key: gwtadmin
end-point: ??
bucket-name: ??
domain: http://192.168.2.13:9000 # 访问域名,注意“/”结尾例如http://minio.abc.com/abc/
base-path: test/ # 基础路径
huawei-obs:
- platform: huawei-obs-1 # 存储平台标识
enable-storage: true # 启用存储
access-key: ??
secret-key: ??
end-point: ??
bucket-name: ??
domain: ?? # 访问域名,注意“/”结尾例如http://abc.obs.com/
base-path: test/ # 基础路径
- platform: huawei-obs-2 # 存储平台标识,这与这里不能重复
enable-storage: true # 启用存储
access-key: ??
secret-key: ??
end-point: ??
bucket-name: ??
domain: ?? # 访问域名,注意“/”结尾例如http://abc.obs.com/
base-path: test2/ # 基础路径
local:
- platform: local-1 # 存储平台标识
enable-storage: true #启用存储
enable-access: true #启用访问(线上请使用 Nginx 配置,效率更高)
domain: ""
base-path: D:/Temp/test/ # 存储地址
path-patterns: /test/file/** # 访问路径,开启 enable-access 后,通过此路径可以访问到上传的文件
mybatis-plus:
mapper-locations: mapper/*.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
type-aliases-package: org.ils.file.entity
## swagger文档的部分配置 ####
knife4j:
# 生产环境可改为 false改为false后 swagger将不能使用
enable: true
logging:
level:
com.baomidou.samples.metainfo: debug

@ -0,0 +1,5 @@
spring:
application:
name: ils-file-storage
profiles:
active: 'dev'

@ -0,0 +1,38 @@
package org.ils.file;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
* Unit test for simple App.
*/
public class AppTest
extends TestCase
{
/**
* Create the test case
*
* @param testName name of the test case
*/
public AppTest( String testName )
{
super( testName );
}
/**
* @return the suite of tests being tested
*/
public static Test suite()
{
return new TestSuite( AppTest.class );
}
/**
* Rigourous Test :-)
*/
public void testApp()
{
assertTrue( true );
}
}
Loading…
Cancel
Save