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服务端进行操作

  • 内置已有的主要接口包括:
  1. TrackerClient - TrackerServer接口
  2. GenerateStorageClient - 一般文件存储接口 (StorageServer接口)
  3. FastFileStorageClient - 为方便项目开发集成的简单接口(StorageServer接口)
  4. 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 文件下载

在这里插入图片描述

————————————————————————

参考资料:

  1. 【官方】FastDFS-Client使用方式&常见问题
  2. 整合FastDFS方式
  3. 使用docker安装fastDFS
posted @ 2021-03-06 16:33  MelodyJerry  阅读(335)  评论(0编辑  收藏  举报
没有伞的孩子必须努力奔跑!|
载入天数...载入时分秒...
(っ•̀ω•́)っ✎⁾⁾ 开心每一天