SpringBoot中使用FastDFS实现文件的上传下载


一、SpringBoot整合FastDFS依赖

FastDFS的依赖在Maven中央仓库中是没有的,需要自己下载源码编译打包。
Githup地址:https://github.com/happyfish100/fastdfs-client-java
Gitee地址:https://gitee.com/fastdfs100/fastdfs-client-java

打包

mvn clean install

在SpringBoot项目中引入依赖

<dependency>
     <groupId>org.csource</groupId>
     <artifactId>fastdfs-client-java</artifactId>
     <version>1.32-SNAPSHOT</version>
</dependency>

二、FastDFS的配置

官方提供了两种配置方式

1. conf 配置文件、所在目录、加载优先顺序

配置文件名fdfs_client.conf(或使用其它文件名xxx_yyy.conf)

文件所在位置可以是项目classpath(或OS文件系统目录比如/opt/):
/opt/fdfs_client.conf
C:\Users\James\config\fdfs_client.conf

优先按OS文件系统路径读取,没有找到才查找项目classpath,尤其针对linux环境下的相对路径比如:
fdfs_client.conf
config/fdfs_client.conf
connect_timeout = 2
network_timeout = 30
charset = UTF-8
http.tracker_http_port = 80
http.anti_steal_token = no
http.secret_key = FastDFS1234567890

tracker_server = 10.0.11.247:22122
tracker_server = 10.0.11.248:22122
tracker_server = 10.0.11.249:22122

connection_pool.enabled = true
connection_pool.max_count_per_entry = 500
connection_pool.max_idle_time = 3600
connection_pool.max_wait_time_in_ms = 1000
注1:tracker_server指向您自己IP地址和端口,1-n个
注2:除了tracker_server,其它配置项都是可选的

2. properties 配置文件、所在目录、加载优先顺序

配置文件名 fastdfs-client.properties(或使用其它文件名 xxx-yyy.properties)

文件所在位置可以是项目classpath(或OS文件系统目录比如/opt/):
/opt/fastdfs-client.properties
C:\Users\James\config\fastdfs-client.properties

优先按OS文件系统路径读取,没有找到才查找项目classpath,尤其针对linux环境下的相对路径比如:
fastdfs-client.properties
config/fastdfs-client.properties
fastdfs.connect_timeout_in_seconds = 5
fastdfs.network_timeout_in_seconds = 30
fastdfs.charset = UTF-8
fastdfs.http_anti_steal_token = false
fastdfs.http_secret_key = FastDFS1234567890
fastdfs.http_tracker_http_port = 80

fastdfs.tracker_servers = 10.0.11.201:22122,10.0.11.202:22122,10.0.11.203:22122

fastdfs.connection_pool.enabled = true
fastdfs.connection_pool.max_count_per_entry = 500
fastdfs.connection_pool.max_idle_time = 3600
fastdfs.connection_pool.max_wait_time_in_ms = 1000
注1:properties 配置文件中属性名跟 conf 配置文件不尽相同,并且统一加前缀"fastdfs.",便于整合到用户项目配置文件
注2:fastdfs.tracker_servers 配置项不能重复属性名,多个 tracker_server 用逗号","隔开
注3:除了fastdfs.tracker_servers,其它配置项都是可选的

3. 加载配置示例

加载原 conf 格式文件配置:
ClientGlobal.init("fdfs_client.conf");
ClientGlobal.init("config/fdfs_client.conf");
ClientGlobal.init("/opt/fdfs_client.conf");
ClientGlobal.init("C:\\Users\\James\\config\\fdfs_client.conf");

加载 properties 格式文件配置:
ClientGlobal.initByProperties("fastdfs-client.properties");
ClientGlobal.initByProperties("config/fastdfs-client.properties");
ClientGlobal.initByProperties("/opt/fastdfs-client.properties");
ClientGlobal.initByProperties("C:\\Users\\James\\config\\fastdfs-client.properties");

加载 Properties 对象配置:
Properties props = new Properties();
props.put(ClientGlobal.PROP_KEY_TRACKER_SERVERS, "10.0.11.101:22122,10.0.11.102:22122");
ClientGlobal.initByProperties(props);

加载 trackerServers 字符串配置:
String trackerServers = "10.0.11.101:22122,10.0.11.102:22122";
ClientGlobal.initByTrackers(trackerServers);

4. 检查加载配置结果

System.out.println("ClientGlobal.configInfo(): " + ClientGlobal.configInfo());
ClientGlobal.configInfo(): {
  g_connect_timeout(ms) = 5000
  g_network_timeout(ms) = 30000
  g_charset = UTF-8
  g_anti_steal_token = false
  g_secret_key = FastDFS1234567890
  g_tracker_http_port = 80
  g_connection_pool_enabled = true
  g_connection_pool_max_count_per_entry = 500
  g_connection_pool_max_idle_time(ms) = 3600000
  g_connection_pool_max_wait_time_in_ms(ms) = 1000
  trackerServers = 10.0.11.101:22122,10.0.11.102:22122
}

三、FastFDS在SpringBoot中的使用

1. 在yml中添加自定义fastdfs配置

fastdfs:
  # 连接fastdfs的ip地址
  tracker-servers: 192.168.241.135:22122
  # 上传图片或文件后访问的基础URL
  access-url: http://192.168.241.135

2. 封装FastDFS文件上传下载工具类

package cn.bear.fastdfs;

import org.csource.common.MyException;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Properties;

/**
 * FastDFS 文件存储客户端
 *
 * @author Job
 */
@Component
public class FastDFSClient {

    @Value("${fastdfs.access-url}")
    private String accessUrl;

    @Value("${fastdfs.tracker-servers}")
    private String trackerServers;

    private StorageClient storageClient;

    /**
     * 初始化存储客户端
     */
    public void initClient() {
        try {
            Properties props = new Properties();
            props.put(ClientGlobal.PROP_KEY_TRACKER_SERVERS, trackerServers);
            ClientGlobal.initByProperties(props);
            TrackerClient tracker = new TrackerClient();
            TrackerServer trackerServer = tracker.getTrackerServer();
            StorageServer storageServer = null;
            storageClient = new StorageClient(trackerServer, storageServer);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (MyException e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 关闭连接客户端
     */
    public void closeClient() {
        if (storageClient != null) {
            try {
                storageClient.close();
            } catch (Exception e) {
                e.printStackTrace();
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 上传文件
     *
     * @param file
     * @return
     */
    public StorePath upload(MultipartFile file) {
        try {
            initClient();
            String filename = file.getOriginalFilename();
            byte[] fileBuff = file.getBytes();
            String fileExtName = filename.substring(filename.lastIndexOf(".") + 1);
            NameValuePair[] metaList = new NameValuePair[2];
            metaList[0] = new NameValuePair("fileName", filename);
            String[] result = storageClient.upload_file(fileBuff, fileExtName, metaList);
            return new StorePath(result[0], result[1]);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (MyException e) {
            throw new RuntimeException(e);
        } finally {
            closeClient();
        }
    }

    /**
     * 上传文件
     *
     * @param groupName 组名,如:group1
     * @param file
     * @return
     */
    public StorePath upload(String groupName, MultipartFile file) {
        try {
            initClient();
            String filename = file.getOriginalFilename();
            byte[] fileBuff = file.getBytes();
            String fileExtName = filename.substring(filename.lastIndexOf(".") + 1);
            NameValuePair[] metaList = new NameValuePair[2];
            metaList[0] = new NameValuePair("fileName", filename);
            String[] result = storageClient.upload_file(groupName, fileBuff, fileExtName, metaList);
            return new StorePath(result[0], result[1]);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (MyException e) {
            throw new RuntimeException(e);
        } finally {
            closeClient();
        }
    }

    /**
     * 上传文件
     *
     * @param fileBuff    文件字节数据
     * @param fileExtName 文件后缀 如 jpeg docx 等
     * @param metaList    可用于存储文件信息,比如文件名称、上传人等
     * @return
     */
    public StorePath upload(byte[] fileBuff, String fileExtName, NameValuePair[] metaList) {
        try {
            initClient();
            String[] result = storageClient.upload_file(fileBuff, fileExtName, metaList);
            return new StorePath(result[0], result[1]);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (MyException e) {
            throw new RuntimeException(e);
        } finally {
            closeClient();
        }
    }

    /**
     * 上传文件
     *
     * @param groupName   集群部署时可使用指定组名上传
     * @param fileBuff    文件字节数据
     * @param fileExtName 文件后缀 如 jpeg docx 等
     * @param metaList    可用于存储文件信息,比如文件名称、上传人等
     * @return
     */
    public StorePath upload(String groupName, byte[] fileBuff, String fileExtName, NameValuePair[] metaList) {
        try {
            initClient();
            String[] result = storageClient.upload_file(groupName, fileBuff, fileExtName, metaList);
            return new StorePath(result[0], result[1]);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (MyException e) {
            throw new RuntimeException(e);
        } finally {
            closeClient();
        }
    }

    /**
     * 下载文件
     *
     * @param fullPath 文件的完整路径,如:group1/M00/00/00/wKjxh2ZhIjmAOQ9jADQRFXpJaxc66.jpeg
     * @param response
     */
    public void download(String fullPath, HttpServletResponse response) {
        StorePath storePath = parseFromUrl(fullPath);
        String groupName = storePath.getGroup();
        String remoteFilename = storePath.getPath();
        // 获取路径中的文件名称
        String fileName = remoteFilename.substring(remoteFilename.lastIndexOf("/") + 1);
        download(fileName, groupName, remoteFilename, response);
    }

    /**
     * 下载文件
     *
     * @param fileName 指定的文件名称,如:test001.jpeg
     * @param fullPath 文件的完整路径,如:group1/M00/00/00/wKjxh2ZhIjmAOQ9jADQRFXpJaxc66.jpeg
     * @param response
     */
    public void specificDownload(String fileName, String fullPath, HttpServletResponse response) {
        StorePath storePath = parseFromUrl(fullPath);
        String groupName = storePath.getGroup();
        String remoteFilename = storePath.getPath();
        download(fileName, groupName, remoteFilename, response);
    }

    /**
     * 下载文件
     *
     * @param groupName      组名,如:group1
     * @param remoteFilename 文件在服务器中的路径,如;M00/00/00/wKjxh2ZhIjmAOQ9jADQRFXpJaxc66.jpeg
     * @param response
     */
    public void download(String groupName, String remoteFilename, HttpServletResponse response) {
        // 获取路径中的文件名称
        String fileName = remoteFilename.substring(remoteFilename.lastIndexOf("/") + 1);
        download(fileName, groupName, remoteFilename, response);
    }

    /**
     * 下载文件
     *
     * @param fileName       指定的文件名称,如:test001.jpeg
     * @param groupName      组名,如:group1
     * @param remoteFilename 文件在服务器中的路径,如;M00/00/00/wKjxh2ZhIjmAOQ9jADQRFXpJaxc66.jpeg
     * @param response
     */
    public void download(String fileName, String groupName, String remoteFilename, HttpServletResponse response) {
        try {
            initClient();
            DownloadStream fileStream = new DownloadStream(response.getOutputStream());
            // 设置发送到客户端的响应的内容类型
            response.setContentType("application/download");
            // 指定客户端下载的文件的名称
            response.setHeader("Content-disposition", "attachment;filename=" + fileName);
            storageClient.download_file(groupName, remoteFilename, fileStream);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (MyException e) {
            throw new RuntimeException(e);
        } finally {
            closeClient();
        }
    }

    /**
     * 删除文件
     *
     * @param fullPath 文件的完整路径,如:group1/M00/00/00/wKjxh2ZhIjmAOQ9jADQRFXpJaxc66.jpeg
     * @return
     */
    public void delete(String fullPath) {
        StorePath storePath = parseFromUrl(fullPath);
        delete(storePath.getGroup(), storePath.getPath());
    }

    /**
     * 删除文件
     *
     * @param groupName      组名,如:group1
     * @param remoteFilename 文件在服务器中的路径,如;M00/00/00/wKjxh2ZhIjmAOQ9jADQRFXpJaxc66.jpeg
     * @return
     */
    public void delete(String groupName, String remoteFilename) {
        try {
            initClient();
            storageClient.delete_file(groupName, remoteFilename);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (MyException e) {
            throw new RuntimeException(e);
        } finally {
            closeClient();
        }
    }

    /**
     * 获取文件的名称信息
     *
     * @param fullPath 文件URL路径,如:group1/M00/00/00/wKjxh2ZhVuuAEAyZAAGkO7PPJ4w858.jpg
     * @return
     */
    public NameValuePair[] getMetadata(String fullPath) {
        StorePath storePath = parseFromUrl(fullPath);
        return getMetadata(storePath.getGroup(), storePath.getPath());
    }

    /**
     * 获取文件的名称信息
     *
     * @param groupName      组名,如:group1
     * @param remoteFilename 文件路径,如:M00/00/00/wKjxh2ZhVuuAEAyZAAGkO7PPJ4w858.jpg
     * @return
     */
    public NameValuePair[] getMetadata(String groupName, String remoteFilename) {
        try {
            initClient();
            return storageClient.get_metadata(groupName, remoteFilename);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (MyException e) {
            throw new RuntimeException(e);
        } finally {
            closeClient();
        }
    }

    /**
     * 查询文件信息
     *
     * @param groupName      组名
     * @param remoteFilename 文件在服务器中的路径,如;M00/00/00/wKjxh2ZhIjmAOQ9jADQRFXpJaxc66.jpeg
     * @return
     */
    public FileInfo queryFileInfo(String groupName, String remoteFilename) {
        try {
            initClient();
            return storageClient.query_file_info(groupName, remoteFilename);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (MyException e) {
            throw new RuntimeException(e);
        } finally {
            closeClient();
        }
    }

    /**
     * 解析文件Url
     *
     * @param fullPath 文件URL路径,如:group1/M00/00/00/wKjxh2ZhVuuAEAyZAAGkO7PPJ4w858.jpg
     * @return
     */
    public StorePath parseFromUrl(String fullPath) {
        if (fullPath.isEmpty()) {
            throw new RuntimeException("解析文件路径不能为空");
        }
        String group = getGroupName(fullPath);
        int pathStartPos = fullPath.indexOf(group) + group.length() + 1;
        String path = fullPath.substring(pathStartPos, fullPath.length());
        return new StorePath(group, path);
    }

    private String getGroupName(String fullPath) {
        String[] paths = fullPath.split("/");
        if (paths.length == 1) {
            throw new RuntimeException("解析文件路径错误,有效的路径样式为(group/path) 而当前解析路径为".concat(fullPath));
        } else {
            String[] var2 = paths;
            int var3 = paths.length;

            for (int var4 = 0; var4 < var3; ++var4) {
                String item = var2[var4];
                if (item.indexOf("group") != -1) {
                    return item;
                }
            }
            throw new RuntimeException("解析文件路径错误,被解析路径url没有group,当前解析路径为".concat(fullPath));
        }
    }

    /**
     * 文件路径响应对象
     */
    public class StorePath {

        private String group;

        private String path;

        public StorePath() {
        }

        public StorePath(String group, String path) {
            this.group = group;
            this.path = path;
        }

        public String getGroup() {
            return this.group;
        }

        public void setGroup(String group) {
            this.group = group;
        }

        public String getPath() {
            return this.path;
        }

        public void setPath(String path) {
            this.path = path;
        }

        public String getFullPath() {
            return this.group.concat("/").concat(this.path);
        }

        public String getAccessUrl() {
            return accessUrl.concat("/").concat(this.group).concat("/").concat(this.path);
        }

        public String toString() {
            return "StorePath [group=" + this.group + ", path=" + this.path + ", fullPath=" + this.getFullPath() + "]";
        }
    }
}

3. FastDFS文件上传下载Controller

package cn.bear.controller;

import cn.bear.basic.controller.BaseController;
import cn.bear.basic.result.AjaxResult;
import cn.bear.fastdfs.FastDFSClient;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.FileInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@RestController
@RequestMapping("/fastDfs")
@Api(tags = "FastDFS文件上传下载")
public class FastDFSController extends BaseController {
    
    @Autowired
    private FastDFSClient fastDFSClient;

    @PostMapping("/upload")
    @ApiOperation("文件上传")
    public AjaxResult upload(@RequestPart MultipartFile file) throws IOException {
        String filename = file.getOriginalFilename();
        byte[] bytes = file.getBytes();
        String fileExtName = filename.substring(filename.lastIndexOf(".") + 1);
        NameValuePair[] metaList = new NameValuePair[2];
        metaList[0] = new NameValuePair("fileName", filename);
        metaList[1] = new NameValuePair("author", "Job");
        FastDFSClient.StorePath storePath = fastDFSClient.upload(bytes, fileExtName, metaList);
        System.out.println(storePath);
        return success(storePath);
    }

    @GetMapping("/download")
    @ApiOperation("文件下载")
    public void downloadClient(String fileName, String groupName, String remoteFilename, HttpServletResponse response) {
        fastDFSClient.download(fileName, groupName, remoteFilename, response);
    }

    @DeleteMapping("/delete")
    @ApiOperation("文件删除")
    public AjaxResult deleteClient(String fullPath) {
        fastDFSClient.delete(fullPath);
        return success();
    }

    @GetMapping("/getMetadata")
    @ApiOperation("获取文件元数据信息")
    public AjaxResult getMetadata(String remoteFilename) {
        NameValuePair[] metadata = fastDFSClient.getMetadata(remoteFilename);
        System.out.println(metadata);
        return success(metadata);
    }

    @GetMapping("/queryFileInfo")
    @ApiOperation("查询文件信息")
    public AjaxResult queryFileInfo(String groupName, String remoteFilename) {
        FileInfo fileInfo = fastDFSClient.queryFileInfo(groupName, remoteFilename);
        System.out.println(fileInfo);
        return success(fileInfo);
    }
}

其他的就是根据实际业务需求继续扩展了

四、FastDFS的安装部署

点击查看:https://blog.csdn.net/m0_72760334/article/details/139558912

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值