SpringBoot+FastDFS+Swagger2整合,快速搭建分布式文件服务器
首发于个人的CSDN:《SpringBoot+FastDFS+Swagger2整合,快速搭建分布式文件服务器》
1.导入依赖
pom.xml
中加入相应的依赖
<!-- FastDFS 依赖 -->
<dependency>
<groupId>com.github.tobato</groupId>
<artifactId>fastdfs-client</artifactId>
<version>1.27.2</version>
</dependency>
<!-- Swagger2 核心依赖 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
2.在application.yml当中配置FastDFS相关参数
- 注意的是:
tracker-list
的参数要修改成目标ip:22122
,也支持多个参数
#SpringBoot配置
spring:
servlet:
multipart:
max-file-size: 100MB #最大支持文件大小
max-request-size: 100MB #最大支持请求大小
# 分布式文件系统FDFS配置
fdfs:
so-timeout: 1501 #读取时间
connect-timeout: 601 #链接超时
thumb-image: #缩略图生成参数
width: 150
height: 150
tracker-list: #TrackerList参数,支持多个
- 192.168.1.105:22122 #自己的ip:22122
#- 192.168.1.106:22122
3.配置文件
3.1 整合Swagger 2
SwaggerConfig.java
/**
* @author MelodyJerry
*/
@Configuration
@EnableSwagger2 //必须存在
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
//注意修改成自己的包路径,不然扫描不到controller
.apis(RequestHandlerSelectors.basePackage("com.melodyjerry.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("SpringBoot利用Swagger2构建API文档")
.description("使用RestFul风格")
.termsOfServiceUrl("https://blog.csdn.net/weixin_43438052/article/details/114288535")
.version("version 1.0.0")
.build();
}
}
3.2 DfsConfig.java
@Configuration
@Import(FdfsClientConfig.class)
// Jmx重复注册bean的问题
@EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)
public class DfsConfig {
}
4.使用内置接口服务对Fdfs服务端进行操作
- 内置已有的主要接口包括:
TrackerClient
- TrackerServer接口GenerateStorageClient
- 一般文件存储接口 (StorageServer接口)FastFileStorageClient
- 为方便项目开发集成的简单接口(StorageServer接口)AppendFileStorageClient
- 支持文件续传操作的接口 (StorageServer接口)
5.工具类 FDFSUtil
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.domain.fdfs.ThumbImageConfig;
import com.github.tobato.fastdfs.domain.proto.storage.DownloadByteArray;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @version 1.0
* @classname FDFSUtil
* @description TODO
* @date 2021/3/2 21:54
* @created by MelodyJerry
*/
@Component
public class FDFSUtil {
@Autowired
private FastFileStorageClient fastFileStorageClient; //为方便项目开发集成的简单接口(StorageServer接口)
@Autowired
private ThumbImageConfig thumbImageConfig; //缩略图
/**
* 上传文件
* @param file 文件对象
* @throws IOException
* @return storePath 文件在服务器中的存储路径 group1/M00/00/08/rBEAA2A_Ci2AU9pHAAYlnPgRYiA554.jpg
* //todo 暂未实现文件分组:aifruit、apple、banana……
*/
public String uploadFile(MultipartFile file) throws IOException {
// StorePath storePath = fastFileStorageClient.uploadFile("aifruit", //默认都是放在aifruit分组group
StorePath storePath = fastFileStorageClient.uploadFile(file.getInputStream(),
file.getSize(),
FilenameUtils.getExtension(file.getOriginalFilename()), null);
return storePath.getFullPath(); //返回完整的图片存放路径(含group)
}
/**
* 下载文件(文件字节)
* @param fileUrl 文件服务器存储路径 group1/M00/00/08/rBEAA2A_Ci2AU9pHAAYlnPgRYiA554.jpg
* @return byte[] 文件字节
* @throws IOException
*/
public byte[] downloadFileByByte(String fileUrl) throws IOException {
String group = fileUrl.substring(0, fileUrl.indexOf("/"));
String path = fileUrl.substring(fileUrl.indexOf("/") + 1);
byte[] bytes = fastFileStorageClient.downloadFile(group, path,
new DownloadByteArray());
return bytes;
}
}
6.控制器 FDFSController
import com.aifruit.fruit.common.util.FDFSUtil;
import com.aifruit.fruit.common.util.PageCodeEnum;
import com.aifruit.fruit.common.vo.ResultVO;
import com.alibaba.druid.util.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import springfox.documentation.annotations.ApiIgnore;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
/**
* @version 1.0
* @classname FDFSController
* @description TODO
* @date 2021/3/2 23:21
* @created by MelodyJerry
*/
@Api(value = "/fdfs", tags = {"FastDFS文件存储交互接口(锐杰)"})
@RestController
@RequestMapping("/fdfs")
public class FDFSController {
//Logger日志管理
private Logger logger = LoggerFactory.getLogger(FDFSController.class);
@Autowired
private FDFSUtil fdfsUtil;
/**
* 上传文件
* @param file 文件对象
* @return
* //todo 暂未实现文件分组:apple、banana……
*/
@ApiOperation(value = "上传文件", notes = "文件上传,返回data即为文件在服务器中的存储路径")
@PostMapping(value = "/uploadFile", headers="content-type=multipart/form-data")
public ResultVO uploadFile(@ApiParam(required = true, name = "file", value = "请选择一个文件")
@RequestParam("file") MultipartFile file) {
String result;
try {
String path = fdfsUtil.uploadFile(file);
if (!StringUtils.isEmpty(path)) {
result = path;
logger.info("文件上传成功: " + path);
} else {
result = "文件上传失败";
logger.info("文件上传失败");
return ResultVO.fail(PageCodeEnum.Deal_Fail, result);
}
} catch (IOException e) {
e.printStackTrace();
result = "文件上传服务异常...";
logger.info("文件上传服务异常...");
return ResultVO.fail(PageCodeEnum.Internal_ERROR, result);
}
return ResultVO.ok(result);
}
/**
* 下载文件(文件字节)
* @param filePath 文件服务器存储路径 group1/M00/00/08/rBEAA2A_Ci2AU9pHAAYlnPgRYiA554.jpg
* @return
*
* //todo 接口可以下载到文件,但控制台提示:
* .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class java.util.LinkedHashMap] with preset Content-Type 'application/octet-stream']
* org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class com.aifruit.fruit.common.vo.ResultVO] with preset Content-Type 'application/octet-stream'
*/
@ApiOperation(value = "文件下载(文件字节+IO流)", notes = "采用 文件字节+IO流方式 实现文件下载.\n获取到文件后,转成文件字节数组,并输出流写入到文件中")
@GetMapping("/downloadFileByByte")
public ResultVO downloadFileByByte(@ApiParam(required = true, name = "filePath", value = "文件在服务器中的存储路径")
@RequestParam("filePath") String filePath, HttpServletResponse response) {
//filePath: group1/M00/00/08/rBEAA2A_Ci2AU9pHAAYlnPgRYiA554.jpg
int lastIndexOf = filePath.lastIndexOf("/");
String fileName = filePath.substring(lastIndexOf + 1);
try {
response.setHeader("content-type", "application/octet-stream");
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
byte[] bytes = fdfsUtil.downloadFileByByte(filePath);
InputStream inputStream = new ByteArrayInputStream(bytes);
byte[] buff = new byte[1024];
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
ServletOutputStream outputStream = response.getOutputStream();
int read = bufferedInputStream.read(buff);
while (read != -1) {
outputStream.write(buff, 0, buff.length);
outputStream.flush(); //刷新此输出流并强制任何缓冲的输出字节被写出
read = bufferedInputStream.read(buff);
}
outputStream.close(); //关闭此输出流并释放与此流相关联的任何系统资源
bufferedInputStream.close(); //必须释放,节省资源
logger.info("文件下载成功");
return ResultVO.ok(PageCodeEnum.Deal_SUCCESS);
} catch (IOException e) {
e.printStackTrace();
logger.info("文件下载服务异常...");
return ResultVO.fail(PageCodeEnum.Internal_ERROR, "文件下载服务异常...");
}
}
}
7.演示
7.1 uploadFile 上传文件
7.2 downloadFileByByte 文件下载
————————————————————————
参考资料:
💥 一个正在学习全栈的 💥
⭐ 精神小伙 ⭐
💥 MelodyJerry 💥