From 7fc241495dba37f98b51936baa0290f6f15f1ad2 Mon Sep 17 00:00:00 2001 From: fst1996 <2411194573@qq.com> Date: Sun, 19 Nov 2023 11:18:45 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 46 +++++ Dockerfile | 17 ++ pom.xml | 114 ++++++++++++ .../java/com/god/file/GodFileApplication.java | 19 ++ .../java/com/god/file/config/MinioConfig.java | 72 ++++++++ .../com/god/file/config/ResourcesConfig.java | 48 ++++++ .../file/controller/SysFileController.java | 43 +++++ .../service/FastDfsSysFileServiceImpl.java | 47 +++++ .../com/god/file/service/ISysFileService.java | 21 +++ .../file/service/LocalSysFileServiceImpl.java | 50 ++++++ .../file/service/MinioSysFileServiceImpl.java | 50 ++++++ .../com/god/file/utils/FileUploadUtils.java | 163 ++++++++++++++++++ src/main/resources/banner.txt | 2 + src/main/resources/bootstrap.yml | 25 +++ src/main/resources/logback.xml | 74 ++++++++ 15 files changed, 791 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 pom.xml create mode 100644 src/main/java/com/god/file/GodFileApplication.java create mode 100644 src/main/java/com/god/file/config/MinioConfig.java create mode 100644 src/main/java/com/god/file/config/ResourcesConfig.java create mode 100644 src/main/java/com/god/file/controller/SysFileController.java create mode 100644 src/main/java/com/god/file/service/FastDfsSysFileServiceImpl.java create mode 100644 src/main/java/com/god/file/service/ISysFileService.java create mode 100644 src/main/java/com/god/file/service/LocalSysFileServiceImpl.java create mode 100644 src/main/java/com/god/file/service/MinioSysFileServiceImpl.java create mode 100644 src/main/java/com/god/file/utils/FileUploadUtils.java create mode 100644 src/main/resources/banner.txt create mode 100644 src/main/resources/bootstrap.yml create mode 100644 src/main/resources/logback.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..09bdfea --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +###################################################################### +# Build Tools + +.gradle +/build/ +!gradle/wrapper/gradle-wrapper.jar + +target/ +!.mvn/wrapper/maven-wrapper.jar + +###################################################################### +# IDE + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### JRebel ### +rebel.xml +### NetBeans ### +nbproject/private/ +build/* +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +###################################################################### +# Others +*.log +*.xml.versionsBackup +*.swp + +!*/build/*.java +!*/build/*.html +!*/build/*.xml \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e35de21 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +# 基础镜像 +FROM anolis-registry.cn-zhangjiakou.cr.aliyuncs.com/openanolis/openjdk:17-8.6 + +#暴露端口位置 +EXPOSE 9300 + +# 挂载目录 +VOLUME /home/logs/god-file + +# 复制jar文件到docker内部 +COPY /target/god-modules-file.jar /home/app.jar + +#工作目录 exec -it 进来默认就是这个目 +WORKDIR /home + +# 启动java程序 +ENTRYPOINT ["java","-Dfile.encoding=UTF-8","-jar","/home/app.jar"] diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..9268c12 --- /dev/null +++ b/pom.xml @@ -0,0 +1,114 @@ + + + + com.god + god-modules + 3.6.3 + + 4.0.0 + + god-modules-file + 3.6.3 + + + god-modules-file文件服务 + + + + + menghang-public + 梦航-public + http://10.100.1.6:8081/repository/maven-public/ + + + + + + menghang-releases + 梦航-releases + http://10.100.1.6:8081/repository/maven-releases/ + + + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + com.github.tobato + fastdfs-client + + + + + io.minio + minio + ${minio.version} + + + + + + com.god + god-common-swagger + + + + + com.god + god-common-system + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + + + diff --git a/src/main/java/com/god/file/GodFileApplication.java b/src/main/java/com/god/file/GodFileApplication.java new file mode 100644 index 0000000..2de0d2b --- /dev/null +++ b/src/main/java/com/god/file/GodFileApplication.java @@ -0,0 +1,19 @@ +package com.god.file; + +import com.god.common.swagger.annotation.EnableCustomSwagger2; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; + +/** + * 文件服务 + * + * @author god + */ +@EnableCustomSwagger2 +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) +public class GodFileApplication { + public static void main (String[] args) { + SpringApplication.run(GodFileApplication.class, args); + } +} diff --git a/src/main/java/com/god/file/config/MinioConfig.java b/src/main/java/com/god/file/config/MinioConfig.java new file mode 100644 index 0000000..4af17eb --- /dev/null +++ b/src/main/java/com/god/file/config/MinioConfig.java @@ -0,0 +1,72 @@ +package com.god.file.config; + +import io.minio.MinioClient; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Minio 配置信息 + * + * @author god + */ +@Configuration +@ConfigurationProperties(prefix = "minio") +public class MinioConfig { + /** + * 服务地址 + */ + private String url; + + /** + * 用户名 + */ + private String accessKey; + + /** + * 密码 + */ + private String secretKey; + + /** + * 存储桶名称 + */ + private String bucketName; + + public String getUrl () { + return url; + } + + public void setUrl (String url) { + this.url = url; + } + + public String getAccessKey () { + return accessKey; + } + + public void setAccessKey (String accessKey) { + this.accessKey = accessKey; + } + + public String getSecretKey () { + return secretKey; + } + + public void setSecretKey (String secretKey) { + this.secretKey = secretKey; + } + + public String getBucketName () { + return bucketName; + } + + public void setBucketName (String bucketName) { + this.bucketName = bucketName; + } + + @Bean + public MinioClient getMinioClient () { + return MinioClient.builder().endpoint(url).credentials(accessKey, secretKey).build(); + } +} diff --git a/src/main/java/com/god/file/config/ResourcesConfig.java b/src/main/java/com/god/file/config/ResourcesConfig.java new file mode 100644 index 0000000..79801e1 --- /dev/null +++ b/src/main/java/com/god/file/config/ResourcesConfig.java @@ -0,0 +1,48 @@ +package com.god.file.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.io.File; + +/** + * 通用映射配置 + * + * @author god + */ +@Configuration +public class ResourcesConfig implements WebMvcConfigurer { + /** + * 资源映射路径 前缀 + */ + @Value("${file.prefix}") + public String localFilePrefix; + /** + * 上传文件存储在本地的根路径 + */ + @Value("${file.path}") + private String localFilePath; + + @Override + public void addResourceHandlers (ResourceHandlerRegistry registry) { + /** 本地文件上传路径 */ + registry.addResourceHandler(localFilePrefix + "/**") + .addResourceLocations("file:" + localFilePath + File.separator); + } + + /** + * 开启跨域 + */ + @Override + public void addCorsMappings (CorsRegistry registry) { + // 设置允许跨域的路由 + registry.addMapping(localFilePrefix + "/**") + // 设置允许跨域请求的域名 + .allowedOrigins("*") + // 设置允许的方法 + .allowedMethods("GET"); + } +} diff --git a/src/main/java/com/god/file/controller/SysFileController.java b/src/main/java/com/god/file/controller/SysFileController.java new file mode 100644 index 0000000..d16ff32 --- /dev/null +++ b/src/main/java/com/god/file/controller/SysFileController.java @@ -0,0 +1,43 @@ +package com.god.file.controller; + +import com.god.common.core.domain.Result; +import com.god.common.core.utils.file.FileUtils; +import com.god.file.service.ISysFileService; +import com.god.common.system.domain.SysFile; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +/** + * 文件请求处理 + * + * @author god + */ +@RestController +public class SysFileController { + private static final Logger log = LoggerFactory.getLogger(SysFileController.class); + + @Autowired + private ISysFileService sysFileService; + + /** + * 文件上传请求 + */ + @PostMapping("upload") + public Result upload (MultipartFile file) { + try { + // 上传并返回访问地址 + String url = sysFileService.uploadFile(file); + SysFile sysFile = new SysFile(); + sysFile.setName(FileUtils.getName(url)); + sysFile.setUrl(url); + return Result.success(sysFile); + } catch (Exception e) { + log.error("上传文件失败", e); + return Result.error(e.getMessage()); + } + } +} diff --git a/src/main/java/com/god/file/service/FastDfsSysFileServiceImpl.java b/src/main/java/com/god/file/service/FastDfsSysFileServiceImpl.java new file mode 100644 index 0000000..759546c --- /dev/null +++ b/src/main/java/com/god/file/service/FastDfsSysFileServiceImpl.java @@ -0,0 +1,47 @@ +package com.god.file.service; + +import com.alibaba.nacos.common.utils.IoUtils; +import com.github.tobato.fastdfs.domain.fdfs.StorePath; +import com.github.tobato.fastdfs.service.FastFileStorageClient; +import com.god.common.core.utils.file.FileTypeUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.InputStream; + +/** + * FastDFS 文件存储 + * + * @author god + */ +@Service +public class FastDfsSysFileServiceImpl implements ISysFileService { + /** + * 域名或本机访问地址 + */ + @Value("${fdfs.domain}") + public String domain; + + @Autowired + private FastFileStorageClient storageClient; + + /** + * FastDfs文件上传接口 + * + * @param file 上传的文件 + * + * @return 访问地址 + * + * @throws Exception + */ + @Override + public String uploadFile (MultipartFile file) throws Exception { + InputStream inputStream = file.getInputStream(); + StorePath storePath = storageClient.uploadFile(inputStream, file.getSize(), + FileTypeUtils.getExtension(file), null); + IoUtils.closeQuietly(inputStream); + return domain + "/" + storePath.getFullPath(); + } +} diff --git a/src/main/java/com/god/file/service/ISysFileService.java b/src/main/java/com/god/file/service/ISysFileService.java new file mode 100644 index 0000000..458c4ef --- /dev/null +++ b/src/main/java/com/god/file/service/ISysFileService.java @@ -0,0 +1,21 @@ +package com.god.file.service; + +import org.springframework.web.multipart.MultipartFile; + +/** + * 文件上传接口 + * + * @author god + */ +public interface ISysFileService { + /** + * 文件上传接口 + * + * @param file 上传的文件 + * + * @return 访问地址 + * + * @throws Exception + */ + public String uploadFile (MultipartFile file) throws Exception; +} diff --git a/src/main/java/com/god/file/service/LocalSysFileServiceImpl.java b/src/main/java/com/god/file/service/LocalSysFileServiceImpl.java new file mode 100644 index 0000000..79b5c43 --- /dev/null +++ b/src/main/java/com/god/file/service/LocalSysFileServiceImpl.java @@ -0,0 +1,50 @@ +package com.god.file.service; + +import com.god.file.utils.FileUploadUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +/** + * 本地文件存储 + * + * @author god + */ +@Primary +@Service +public class LocalSysFileServiceImpl implements ISysFileService { + /** + * 资源映射路径 前缀 + */ + @Value("${file.prefix}") + public String localFilePrefix; + + /** + * 域名或本机访问地址 + */ + @Value("${file.domain}") + public String domain; + + /** + * 上传文件存储在本地的根路径 + */ + @Value("${file.path}") + private String localFilePath; + + /** + * 本地文件上传接口 + * + * @param file 上传的文件 + * + * @return 访问地址 + * + * @throws Exception + */ + @Override + public String uploadFile (MultipartFile file) throws Exception { + String name = FileUploadUtils.upload(localFilePath, file); + String url = domain + localFilePrefix + name; + return url; + } +} diff --git a/src/main/java/com/god/file/service/MinioSysFileServiceImpl.java b/src/main/java/com/god/file/service/MinioSysFileServiceImpl.java new file mode 100644 index 0000000..7a2a374 --- /dev/null +++ b/src/main/java/com/god/file/service/MinioSysFileServiceImpl.java @@ -0,0 +1,50 @@ +package com.god.file.service; + +import com.alibaba.nacos.common.utils.IoUtils; +import com.god.file.config.MinioConfig; +import com.god.file.utils.FileUploadUtils; +import io.minio.MinioClient; +import io.minio.PutObjectArgs; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.InputStream; + +/** + * Minio 文件存储 + * + * @author god + */ +@Service +public class MinioSysFileServiceImpl implements ISysFileService { + @Autowired + private MinioConfig minioConfig; + + @Autowired + private MinioClient client; + + /** + * Minio文件上传接口 + * + * @param file 上传的文件 + * + * @return 访问地址 + * + * @throws Exception + */ + @Override + public String uploadFile (MultipartFile file) throws Exception { + String fileName = FileUploadUtils.extractFilename(file); + InputStream inputStream = file.getInputStream(); + PutObjectArgs args = PutObjectArgs.builder() + .bucket(minioConfig.getBucketName()) + .object(fileName) + .stream(inputStream, file.getSize(), -1) + .contentType(file.getContentType()) + .build(); + client.putObject(args); + IoUtils.closeQuietly(inputStream); + return minioConfig.getUrl() + "/" + minioConfig.getBucketName() + "/" + fileName; + } +} diff --git a/src/main/java/com/god/file/utils/FileUploadUtils.java b/src/main/java/com/god/file/utils/FileUploadUtils.java new file mode 100644 index 0000000..1a5a7d4 --- /dev/null +++ b/src/main/java/com/god/file/utils/FileUploadUtils.java @@ -0,0 +1,163 @@ +package com.god.file.utils; + +import com.god.common.core.exception.file.FileException; +import com.god.common.core.exception.file.FileNameLengthLimitExceededException; +import com.god.common.core.exception.file.FileSizeLimitExceededException; +import com.god.common.core.exception.file.InvalidExtensionException; +import com.god.common.core.utils.DateUtils; +import com.god.common.core.utils.StringUtils; +import com.god.common.core.utils.file.FileTypeUtils; +import com.god.common.core.utils.file.MimeTypeUtils; +import com.god.common.core.utils.uuid.Seq; +import org.apache.commons.io.FilenameUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Objects; + +/** + * 文件上传工具类 + * + * @author god + */ +public class FileUploadUtils { + /** + * 默认大小 50M + */ + public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024; + + /** + * 默认的文件名最大长度 100 + */ + public static final int DEFAULT_FILE_NAME_LENGTH = 100; + + /** + * 根据文件路径上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * + * @return 文件名称 + * + * @throws IOException + */ + public static final String upload (String baseDir, MultipartFile file) throws IOException { + try { + return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } catch (FileException fe) { + throw new IOException(fe.getDefaultMessage(), fe); + } catch (Exception e) { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 文件上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @param allowedExtension 上传文件类型 + * + * @return 返回上传成功的文件名 + * + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws FileNameLengthLimitExceededException 文件名太长 + * @throws IOException 比如读写文件出错时 + * @throws InvalidExtensionException 文件校验异常 + */ + public static final String upload (String baseDir, MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, + InvalidExtensionException { + int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length(); + if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) { + throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); + } + + assertAllowed(file, allowedExtension); + + String fileName = extractFilename(file); + + String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath(); + file.transferTo(Paths.get(absPath)); + return getPathFileName(fileName); + } + + /** + * 编码文件名 + */ + public static final String extractFilename (MultipartFile file) { + return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(), + FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), FileTypeUtils.getExtension(file)); + } + + private static final File getAbsoluteFile (String uploadDir, String fileName) throws IOException { + File desc = new File(uploadDir + File.separator + fileName); + + if (!desc.exists()) { + if (!desc.getParentFile().exists()) { + desc.getParentFile().mkdirs(); + } + } + return desc.isAbsolute() ? desc : desc.getAbsoluteFile(); + } + + private static final String getPathFileName (String fileName) throws IOException { + String pathFileName = "/" + fileName; + return pathFileName; + } + + /** + * 文件大小校验 + * + * @param file 上传的文件 + * + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws InvalidExtensionException 文件校验异常 + */ + public static final void assertAllowed (MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, InvalidExtensionException { + long size = file.getSize(); + if (size > DEFAULT_MAX_SIZE) { + throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024); + } + + String fileName = file.getOriginalFilename(); + String extension = FileTypeUtils.getExtension(file); + if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) { + if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) { + throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, + fileName); + } else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) { + throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, + fileName); + } else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) { + throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, + fileName); + } else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) { + throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension, + fileName); + } else { + throw new InvalidExtensionException(allowedExtension, extension, fileName); + } + } + } + + /** + * 判断MIME类型是否是允许的MIME类型 + * + * @param extension 上传文件类型 + * @param allowedExtension 允许上传文件类型 + * + * @return true/false + */ + public static final boolean isAllowedExtension (String extension, String[] allowedExtension) { + for (String str : allowedExtension) { + if (str.equalsIgnoreCase(extension)) { + return true; + } + } + return false; + } +} diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt new file mode 100644 index 0000000..0dd5eee --- /dev/null +++ b/src/main/resources/banner.txt @@ -0,0 +1,2 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} diff --git a/src/main/resources/bootstrap.yml b/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..abbdae6 --- /dev/null +++ b/src/main/resources/bootstrap.yml @@ -0,0 +1,25 @@ +# Tomcat +server: + port: 9300 + +# Spring +spring: + application: + # 应用名称 + name: god-file + profiles: + # 环境配置 + active: dev + cloud: + nacos: + discovery: + # 服务注册地址 + server-addr: nacos.god.com:8848 + config: + # 配置中心地址 + server-addr: nacos.god.com:8848 + # 配置文件格式 + file-extension: yml + # 共享配置 + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..ba5edfb --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + +