一、背景说明
文件、图片上传下载是一个非常常用的功能。微服务也是一个轻量级应用。将二者融合,把文件的上传下载功能做成微服务,使此功能独立于业务功能代码之外。可以方便切换与不同的项目之间,使上传下载功能和其他应用实现解耦合的同时,更方便与我们的管理。
二、方案说明
第一套方案为:微服务 + IO流自己定义上传下载功能
优点:配置需要简单,搭建容易,只需要一台大容量服务器即可实现功能,不需要nginx。全程用流处理文件,代码量较多。
项目使用spring cloud alibaba微服务 nacos做配置中心 feign+Ribbon做远程调用代理 sentinel做熔断处理。微服务使用版本spring cloud alibaba 版本 2021.1 ==> Spring Boot 版本 2.4.2 ==> Spring Cloud版本 2020.0.2
微服务接口按照统一出参入参格式管理
微服务接口按照swagger对外提供接口
第二套方案:微服务 + fastdfs
优点:fastdfs 是一个比较成熟的分布式做文件服务器的一个应用。使用起来比较简单,代码量少,需要配置条件较多 需要配置Nginx+FastDfs+微服务。
本文先使用第一种方案
三、环境配置管理
前提准备:
1、需要部署Nacos
2、准备一台Linux服务器即可
四、代码管理
主要代码量为 文件服务器: 微服务+IO流
应用方:微服务
其中IO流方案,具体描述已在博客中详细写过,本文主要介绍一下微服务之间的调用关系。少量篇幅来介绍IO流上传下载。参考:(7条消息) springboot文件上传与下载_无敌小田田的博客-CSDN博客https://blog.csdn.net/qq_36602951/article/details/119452787
1、新建gradle 的spring boot项目:filemanage 项目名称
代码路径如下:
build.gradle文件
plugins {
id 'org.springframework.boot' version '2.4.2'
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
id 'java'
}
group 'com.cicccrm'
version '1.0.0'
repositories {
mavenCentral()
maven { url 'http://nexus.cicconline.com/repository/maven-public'}
maven { url 'https://repo.spring.io/milestone' }
maven { url 'https://repo.spring.io/snapshot' }
}
ext {
springCloudVersion = "2020.0.2"
springBootVersion = "2.4.2"
comAlibabaCloud = "2021.1"
}
configurations {
all*.exclude module:'spring-boot-starter-logging'
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
compile("org.springframework.boot:spring-boot-starter")
{
exclude module: 'org.springframework.boot:spring-boot-starter-logging'
}
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation group: 'com.alibaba.boot', name: 'nacos-config-spring-boot-starter', version: '0.2.1'
compile group: 'io.springfox', name: 'springfox-swagger2', version: '3.0.0'
compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '3.0.0'
implementation group: 'com.alibaba.cloud', name: 'spring-cloud-starter-alibaba-nacos-discovery', version: '2021.1'
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-log4j2'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-actuator'
testCompile group: 'junit', name: 'junit', version: '4.12'
implementation group: 'org.springframework.session', name: 'spring-session-core', version: '2.4.2'
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.10'
implementation group: 'com.alibaba', name: 'fastjson', version: '1.2.41'
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
mavenBom "org.springframework.boot:spring-boot-dependencies:${springBootVersion}"
}
}
test {
useJUnitPlatform()
}
2、application.properties配置文件
server.port=8088 webRoot=http://localhost:8088 spring.application.name=fengqx-fileManager #nacos地址 spring.cloud.nacos.discovery.server-addr=1.2.3.4:8848 #nacos命名空间 spring.cloud.nacos.discovery.namespace=******* #nacos默认组 spring.cloud.nacos.discovery.group=DEFAULT_GROUP management.endpoints.jmx.exposure.include=* management.endpoints.web.exposure.include=* management.endpoints.health.show-details=always #日志管理log4j2 logging.config=classpath:log4j2.xml #文件保存路径 jar所在路径+/oneRoomFile file.save.path=filePath #文件上传大小配置 spring.servlet.multipart.max-file-size = 5MB spring.servlet.multipart.max-request-size = 50MB
3、Main 函数 FileApplication.java
package com.*****.fileManage;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class FileApplication {
public static void main(String[] args) {
SpringApplication.run(FileApplication.class, args);
}
}
4、gradle中还设置了swagger,需要增加swagger的配置文件
com.***.fileManage.annotation下 ApiVersion.java 接口
package com.****.fileManage.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ApiVersion {
String[] value();
}
com.****.fileManage.config下 ApiVersionConstant.java接口
package com.*****.fileManage.config;
public interface ApiVersionConstant {
String ONE_CRM_SPRINT_10 = "v-sprint10";
String ONE_CRM_SPRINT_11 = "v-sprint11";
}
com.****.fileManage.config下 SwaggerConfig.java配置类
package com.****.fileManage.config;
import com.****.fileManage.annotation.ApiVersion;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.documentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.web.UiConfiguration;
import springfox.documentation.swagger.web.UiConfigurationBuilder;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.lang.reflect.Field;
import java.util.Arrays;
@Configuration
@EnableSwagger2
@EnableWebMvc
public class SwaggerConfig extends WebMvcConfigurerAdapter implements InitializingBean {
@Autowired
private ApplicationContext applicationContext;
// @Value("${swagger.remark}")
//private String swaggerRemark;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.
addResourceHandler("/swagger-ui
@Bean
public Docket createRestApi() {
return new Docket(documentationType.SWAGGER_2)
.groupName("CrmApi")
.genericModelSubstitutes(DeferredResult.class)
// .genericModelSubstitutes(ResponseEntity.class)
.useDefaultResponseMessages(false)
.forCodeGeneration(true)
.select()
.apis(RequestHandlerSelectors.basePackage("com.cicccrm.fileManage.controller"))
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo());
//com.cicc.gwms.dataSummary,
}
private Docket buildDocket(String groupName) {
return new Docket(documentationType.SWAGGER_2)
.apiInfo(apiInfo())
.groupName(groupName)
.select()
.apis(method -> {
// 每个方法会进入这里进行判断并归类到不同分组,**请不要调换下面两段代码的顺序,在方法上的注解有优先级**
// 该方法上标注了版本
if (method.isAnnotatedWith(ApiVersion.class)) {
ApiVersion apiVersion = method.getHandlerMethod().getMethodAnnotation(ApiVersion.class);
if (apiVersion.value() != null && apiVersion.value().length != 0) {
if (Arrays.asList(apiVersion.value()).contains(groupName)) {
return true;
}
}
}
// 方法所在的类是否标注了?
ApiVersion annotationOnClass = method.getHandlerMethod().getBeanType().getAnnotation(ApiVersion.class);
if (annotationOnClass != null) {
if (annotationOnClass.value() != null && annotationOnClass.value().length != 0) {
if (Arrays.asList(annotationOnClass.value()).contains(groupName)) {
return true;
}
}
}
return false;
})
.paths(PathSelectors.any())
.build();
}
//
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("调用服务接口")
// .description(swaggerRemark)
.termsOfServiceUrl(webRoot + "/v2/api-docs")
.version("1.0")
.build();
}
@Bean
public UiConfiguration uiConfig() {
return UiConfigurationBuilder.builder()
.displayRequestDuration(true)
.validatorUrl("")
.build();
}
@Override
public void afterPropertiesSet() throws Exception {
// ApiConstantVersion 里面定义的每个变量会成为一个docket
Class clazz = ApiVersionConstant.class;
Field[] declaredFields = clazz.getDeclaredFields();
// 动态注入bean
AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
if (autowireCapableBeanFactory instanceof DefaultListableBeanFactory) {
DefaultListableBeanFactory capableBeanFactory = (DefaultListableBeanFactory) autowireCapableBeanFactory;
for (Field declaredField : declaredFields) {
// 要注意 "工厂名和方法名",意思是用这个bean的指定方法创建docket
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
.genericBeanDefinition()
.setFactoryMethodonBean("buildDocket", "swaggerConfig")
.addConstructorArgValue(declaredField.get(ApiVersionConstant.class)).getBeanDefinition();
capableBeanFactory.registerBeanDefinition(declaredField.getName(), beanDefinition);
}
}
}
}
com.****.fileManage.config下WebConfig.java文件
package com.****.fileManage.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
// 增加映射关系
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/index").setViewName("redirect:/swagger-ui/index.html");
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("
public class ApibaseMessage {
boolean success;
String statusCode = "";
T data;
String error = "";
public ApibaseMessage() {}
public ApibaseMessage(T data) {
this.success = true;
this.data = data;
}
public static com.****.fileManage.common.model.ApibaseMessage getOperationSucceedInstance(Object data)
{
return getSucceedInstance("30001", data);
}
public static com.****.fileManage.common.model.ApibaseMessage getOperationFailedInstance()
{
return getFailedInstance("30002");
}
public static com.****.fileManage.common.model.ApibaseMessage getSucceedInstance(String statusCode, Object data)
{
com.****.fileManage.common.model.ApibaseMessage instance = new com.****.fileManage.common.model.ApibaseMessage(statusCode, data);
instance.setSuccess(true);
return instance;
}
public static com.****.fileManage.common.model.ApibaseMessage getFailedInstance(String statusCode, Object data)
{
com.****.fileManage.common.model.ApibaseMessage instance = new com.****.fileManage.common.model.ApibaseMessage(statusCode, data);
instance.setSuccess(false);
return instance;
}
public static com.****.fileManage.common.model.ApibaseMessage getFailedInstance(String statusCode)
{
com.****.fileManage.common.model.ApibaseMessage instance = new com.****.fileManage.common.model.ApibaseMessage(statusCode);
instance.setSuccess(false);
instance.setData("");
return instance;
}
public ApibaseMessage(String statusCode, T data) {
this.statusCode = statusCode;
this.error = ApiStatusCodeList.getMessage(statusCode);
this.data = data;
}
public ApibaseMessage(String statusCode) {
this.statusCode = statusCode;
this.error = ApiStatusCodeList.getMessage(statusCode);
}
public ApibaseMessage(String statusCode, String error) {
this.statusCode = statusCode;
this.error = error;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public String getStatusCode() {
return statusCode;
}
public void setStatusCode(String statusCode) {
this.statusCode = statusCode;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
}
com.****.fileManage.common.model中的 ApiStatusCodeList.java
package com.****.fileManage.common.model;
import java.util.HashMap;
import java.util.Locale;
public class ApiStatusCodeList {
private static HashMap codeMap = null;
private static final String[][] CODE_ARRAY = {
// 账号
{"10001", "账号输入为空", "The account is not presented."},
{"10002", "用户名或密码错误", ""},
{"10003", "登录失败", ""},
{"10004", "用户账号不存在", ""},
{"10005", "用户账号状态异常", ""},
{"10006", "账号已冻结", ""},
{"10007", "非IC账号", ""},
{"10008", "账号已登出", ""},
{"10009", "不能进行该操作", ""},
{"10010", "当前登录设备与系统记录不符,请联系管理员", ""},
{"10020", "您目前还没有资产配置建议", ""},
{"20001","文件不存在", ""},
{"20002","下载文件失败", ""},
//操作成功
{"30001", "操作成功", ""},
{"30002", "操作失败", ""},
// 安全
{"70001", "验证码不正确", ""},
// 来自app服务的错误
{"90001", "服务端错误", ""},
{"90002", "权限错误", ""},
{"90003", "您尚未拥有该操作权限", ""}
};
public static String getMessage(String code) {
HashMap codeMapInstance = getCodeMapInstance();
CodeMessage codeMessage = codeMapInstance.get(code);
return codeMessage.getZhCn();
}
public static String getMessage(String code, Locale locale){
HashMap codeMapInstance = getCodeMapInstance();
CodeMessage codeMessage = codeMapInstance.get(code);
switch(locale.toString()){
case "en":
return codeMessage.getEn();
default:
return codeMessage.getZhCn();
}
}
private static HashMap getCodeMapInstance(){
if(codeMap == null){
codeMap = genCodeMap();
}
return codeMap;
}
private static HashMap genCodeMap(){
codeMap = new HashMap<>();
for (String[] aCodeArray : CODE_ARRAY) {
CodeMessage m = new CodeMessage();
m.setStatusCode(aCodeArray[0]);
// zh-cn is the first (1).
m.setZhCn(aCodeArray[1]);
m.setEn(aCodeArray[2]);
codeMap.put(aCodeArray[0], m);
}
return codeMap;
}
public static class CodeMessage {
private String statusCode;
private String zhCn;
private String zhTw;
private String en;
public String getZhCn() {
return zhCn;
}
public void setZhCn(String zhCn) {
this.zhCn = zhCn;
}
public String getZhTw() {
return zhTw;
}
public void setZhTw(String zhTw) {
this.zhTw = zhTw;
}
public String getEn() {
return en;
}
public void setEn(String en) {
this.en = en;
}
public String getStatusCode() {
return statusCode;
}
public void setStatusCode(String statusCode) {
this.statusCode = statusCode;
}
}
}
com.****.fileManage.common.model中的JsonResult.java
package com.****.fileManage.common.model; public class JsonResult{ private int code; private String msg; private Object data; public JsonResult() { } public JsonResult(int code, String msg, T data){ this.code=code; this.data=data; this.msg=msg; } public static com.****.fileManage.common.model.JsonResult jsonErrorMsg (int code, String msg){ return new com.****.fileManage.common.model.JsonResult(code,msg); } public static com.****.fileManage.common.model.JsonResult jsonSuccessMsg (int code, String msg, Object data){ return new com.****.fileManage.common.model.JsonResult(code,msg,data); } public static com.****.fileManage.common.model.JsonResult jsonSuccessMsg (int code, String msg){ return new com.****.fileManage.common.model.JsonResult(code,msg); } public JsonResult(int code, String msg){ this.code = code; this.msg = msg; } public void setCode(int code) { this.code = code; } public int getCode() { return code; } public void setMsg(String msg) { this.msg = msg; } public String getMsg() { return msg; } public Object getData() { return data; } public void setData(T data) { this.data = data; } }
后续出参,使用 ApibaseMessage 实体就可以统一出参了。
6、文件上传下载接口
com.****.fileManage.common.model 中的 FilePath.java 文件
package com.****.fileManage.common.model;
import io.swagger.annotations.ApiModelProperty;
public class FilePath {
@ApiModelProperty(value = "id")
private String id;
@ApiModelProperty(value = "文件分类")
private String fileType;
@ApiModelProperty(value = "文件名")
private String fileName;
@ApiModelProperty(value = "文件路径")
private String filePath;
@ApiModelProperty(value = "依赖id")
private String relyId;
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFileType() {
return fileType;
}
public void setFileType(String fileType) {
this.fileType = fileType;
}
public String getRelyId() {
return relyId;
}
public void setRelyId(String relyId) {
this.relyId = relyId;
}
@Override
public String toString() {
return "FilePath{" +
"id='" + id + ''' +
", fileType='" + fileType + ''' +
", fileName='" + fileName + ''' +
", filePath='" + filePath + ''' +
", relyId='" + relyId + ''' +
'}';
}
}
com.****.fileManage.util中的 FileUtils.java
package com.****.fileManage.common.util;
import com.****.fileManage.common.model.FilePath;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.UUID;
public class FileUtils {
private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(FileUtils.class);
public static Builder setPathName(String pathName) {
return builder().setPathName(pathName);
}
public static Builder setMaxSize(int maxSize) {
return builder().setMaxSize(maxSize);
}
public static Builder setMaxFileNameLength(int maxFileNameLength) {
return builder().setMaxFileNameLength(maxFileNameLength);
}
public static Builder setAllowedExtension(String[] allowedExtension) {
return builder().setAllowedExtension(allowedExtension);
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private long defaultMaxSize = 5 * 1024 * 1024;
private int defaultFileNameLength = 100;
private String[] defaultAllowedExtension = {"pdf", "xlsx", "xls", "docx", "doc", "ppt", "pptx"
,"jpg","jpeg","png","gif","bmp"};
private String pathName;
public Builder setMaxSize(int maxSize) {
this.defaultMaxSize = (long) maxSize * 1024 * 1024;
return this;
}
public Builder setMaxFileNameLength(int maxFileNameLength) {
this.defaultFileNameLength = maxFileNameLength;
return this;
}
public Builder setAllowedExtension(String[] allowedExtension) {
this.defaultAllowedExtension = allowedExtension;
return this;
}
public Builder setPathName(String pathName) {
this.pathName = pathName;
return this;
}
public final FilePath upload(MultipartFile file) throws Exception {
//校验
if (StringUtils.trimTonull(pathName) == null) {
throw new IOException("文件保存路径为空");
}
String extensionFilename = FilenameUtils.getExtension(file.getOriginalFilename());
if (!ArrayUtils.isEmpty(defaultAllowedExtension) && !ArrayUtils.contains(defaultAllowedExtension, extensionFilename)) {
throw new IOException("只允许" + ArrayUtils.toString(defaultAllowedExtension) + "文件上传");
}
if (file.getSize() > defaultMaxSize) {
throw new IOException("文件过大");
}
if (file.getOriginalFilename().length() > defaultFileNameLength) {
throw new IOException("文件名过长");
}
//上传
String fileName = extractFilename(file);
File desc = getAbsoluteFile(pathName, fileName);
try {
file.transferTo(desc);
} catch (IllegalStateException e) {
LOG.error("文件上传出现错误" + e.getLocalizedMessage());
e.printStackTrace();
throw e;
} catch (IOException e) {
LOG.error("文件上传出现错误" + e.getLocalizedMessage());
e.printStackTrace();
throw e;
}
FilePath filePath = new FilePath();
filePath.setFileName(file.getOriginalFilename());
filePath.setFilePath(pathName + File.separator + fileName);
return filePath;
}
private final String extractFilename(MultipartFile file) {
String filename = file.getOriginalFilename();
// String extension = getExtension(file);
filename = DateFormatUtils.format(new Date(), "yyyy-MM-dd") + File.separator + getFinalFileName(file);
return filename;
}
// private final String encodingFilename(String filename) {
// filename = filename.replace("_", " ");
// filename = DigestUtils.md5Hex(filename + System.nanoTime() + RandomStringUtils.randomNumeric(6));
// return filename;
// }
public final String getExtension(MultipartFile file) {
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
return extension;
}
public String getFinalFileName(MultipartFile file) {
//获取后缀
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
//获取前缀
String removeExtension = FilenameUtils.removeExtension(file.getOriginalFilename());
//获取随机字符串+时间戳
String extra = UUID.randomUUID().toString().replaceAll("-", "");
return removeExtension + extra + "." + extension;
}
private final File getAbsoluteFile(String uploadDir, String filename) throws IOException {
try {
//获取jar根路径
ApplicationHome h = new ApplicationHome(getClass());
File jarF = h.getSource();
File desc = new File(jarF.getParentFile().toString() + File.separator + uploadDir + File.separator + filename);
LOG.error("file===" + jarF.getParentFile().toString() + File.separator + uploadDir + File.separator + filename);
try {
if (!desc.getParentFile().exists()) {
desc.getParentFile().mkdirs();
}
if (!desc.exists()) {
desc.createNewFile();
}
} catch (Exception e) {
LOG.error(e.getMessage());
throw e;
}
return desc;
} catch (Exception ex) {
throw ex;
}
}
public static ResponseEntity download( String filePath) throws IOException {
File file = new File(filePath);//新建一个文件
HttpHeaders headers = new HttpHeaders();//http头信息
String downloadFileName = new String(filePath.getBytes("UTF-8"), "UTF-8");//设置编码
headers.setContentDispositionFormData("attachment", downloadFileName);
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
//MediaType:互联网媒介类型 contentType:具体请求中的媒体类型信息
return new ResponseEntity(org.apache.commons.io.FileUtils.readFileToByteArray(file), headers, HttpStatus.CREATED);
}
public static boolean deleteFile(String filePath) throws IOException {
File file = new File(filePath);
if (!file.exists()) { //不存在返回false
throw new IOException("不存在的文件");
} else {
file.delete();
return true;
}
}
}
}
文件上传fileController.java 文件 上传、下载、删除接口
package com.****.fileManage.controller;
import com.alibaba.fastjson.JSONObject;
import com.****.fileManage.annotation.ApiVersion;
import com.****.fileManage.common.model.ApibaseMessage;
import com.****.fileManage.common.model.FilePath;
import com.****.fileManage.common.model.JsonResult;
import com.****.fileManage.common.util.FileUtils;
import com.****.fileManage.config.ApiVersionConstant;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.linkedList;
import java.util.List;
@RestController
@RequestMapping("/file")
public class FileUploadController {
private static final org.slf4j.Logger log=org.slf4j.LoggerFactory.getLogger(com.****.fileManage.controller.FileUploadController.class);
@Value("${file.save.path}")
private String defaultPath;
@ApiOperation(value = "上传文件 - 单个")
@ApiVersion({ApiVersionConstant.ONE_CRM_SPRINT_10})
@RequestMapping(value = "upload", method = {RequestMethod.POST})
@ResponseBody
public ApibaseMessage fileUpload(@RequestParam("file") MultipartFile file, @RequestParam("type") String type, HttpServletRequest request){
try {
log.info("上传文件 - 单个开始");
//这个别的微服务的接口,接收图片使用的参数名是headPortrait
Collection parts = request.getParts();
log.info(JSONObject.toJSonString(parts, true));
List uploadFile=new linkedList();
String path=defaultPath+File.separator+type.toUpperCase();
FilePath fileResult = FileUtils.builder().setPathName(path).upload(file);
uploadFile.add(fileResult);
log.info("上传文件 - 单个完成");
return ApibaseMessage.getSucceedInstance("30001", uploadFile);
} catch (IOException e) {
return ApibaseMessage.getSucceedInstance("30002", JsonResult.jsonErrorMsg(203,e.getMessage()));
} catch (Exception e){
return ApibaseMessage.getSucceedInstance("30002", JsonResult.jsonErrorMsg(500,e.getMessage()));
}
}
@ApiOperation(value = "上传文件 - 多个")
@ApiVersion({ApiVersionConstant.ONE_CRM_SPRINT_10})
@ApiImplicitParams({
@ApiImplicitParam(paramType = "query", name = "type", value = "文件分类", dataType = "String")
})
@RequestMapping(value = "MultiUpload",method = {RequestMethod.POST})
@ResponseBody
public ApibaseMessage multiFileUpload(@RequestParam("files") MultipartFile[] files, @RequestParam("type") String type, HttpServletRequest request){
try {
log.info("上传文件 - 多个开始");
//这个别的微服务的接口,接收图片使用的参数名是headPortrait
Collection parts = request.getParts();
log.info(JSONObject.toJSonString(parts, true));
String path=defaultPath+File.separator+type.toUpperCase()+File.separator;
List uploadFile=new linkedList();
if(files!=null&&files.length>0){
//循环获取file数组中得文件
for(int i = 0;i download(@RequestParam String filePath) throws Exception {
try {
log.info("下载文件 - 开始");
log.info("下载文件路径:" + filePath);
ResponseEntity result = FileUtils.builder().download(filePath);
log.info("下载文件 - 完成");
return result;
}catch (Exception e){
throw e;
}
}
@ApiOperation(value = "删除文件")
@ApiVersion({ApiVersionConstant.ONE_CRM_SPRINT_10})
@ApiImplicitParams({
@ApiImplicitParam(paramType = "query", name = "filePath", value = "文件路径", dataType = "String")
})
@RequestMapping(value = "delete",method = {RequestMethod.DELETE})
@ResponseBody
public ApibaseMessage deleteFile(@RequestParam String filePath) {
try {
log.info("删除文件 - 开始");
log.info("删除文件-文件路径:"+filePath);
boolean ifdelete = FileUtils.builder().deleteFile(filePath);
log.info("删除文件 - 完成");
return ApibaseMessage.getSucceedInstance("30001", ifdelete?"删除成功":"不存在的文件");
}catch (IOException e) {
return ApibaseMessage.getSucceedInstance("30002", JsonResult.jsonErrorMsg(204,e.getMessage()));
} catch (Exception e){
return ApibaseMessage.getSucceedInstance("30002", JsonResult.jsonErrorMsg(500,e.getMessage()));
}
}
}
7、综上完毕
文件服务器端代码,完成。将代码打包成jar,启动即可。启动脚本如下:
startup.sh 和 restart.sh
#!/bin/bash java -jar ./filemanager-1.0.0.jar
ps -ef | grep filemanager-1.0.0.jar | grep -v grep | awk '{print $2}' | xargs kill -9
rm -rf nohup.out
nohup ./startup.sh > nohup.out &
至此文件服务器搭建完成,第二步骤就是应用端调用微服务接口了。参考下面文章!(7条消息) springboot文件服务器 二(IO流+微服务包含swagger、出入参统一格式、log4j2日志)_无敌小田田的博客-CSDN博客https://blog.csdn.net/qq_36602951/article/details/120772314



