针对有比较多的视频、图片或文件的网站,文件上传和存储是必不可少的。

在《文件管理系统FastDFS原理详解》一文中介绍了FastDFS相关的原理。在《FastDFS基于Docker安装,免踩坑版》一文中介绍了基于Docker的安装。

这篇文章带领大家来基于Spring Boot集成FastDFS,并实现上传和访问的功能。

客户端

FastDFS官方提供了一个Java版本的客户端,地址为:https://github.com/happyfish100/fastdfs-client-java

但该客户端有几个问题:第一,命名和方法等没有按照Java的规范来;第二,不支持直接从maven中央仓库获取,需要install到本地;第三,异常处理和配置文件等有待优化的地方。

因此,个人从这个客户端fork了一个分支,然后在此基础上针对以上问题进行了初步优化,后续根据实践需要将进一步优化,而本教程也是基于优化的第一个版本来进行讲解。

该版本在使用时与原始版本体现出来的唯一不同就是方法名该为符合java规范的规则,其他内部改动不影响api层面。

下面首先可以在maven项目中直接引入如下依赖:

<dependency>
    <groupId>com.github.secbr</groupId>
    <artifactId>fastdfs-client-plus</artifactId>
    <version>1.1.1-RELEASE</version>
</dependency>

针对Spring Boot部分,主要引入如下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

引入thymeleaf主要为了支持通过web上传功能。

配置文件

在client的源代码中已经提供了对应的fdfs_client.conf.sample文件,可参考该配置文件在resources目录下添加fdfs_client.conf文件:

 # 连接超时时间
connect_timeout = 60
 # 网络超时时间
network_timeout = 60
 # 编码格式
charset = UTF-8
 #tracker 端口
http.tracker_http_port = 8888
 #token 防盗链功能
http.anti_steal_token = no
 #密钥
http.secret_key = 123456

# tracer server 列表,多个 tracer server 的话,分行列出
#tracker_server = 192.168.0.101:22122
tracker_server = 192.168.43.143:22122
#tracker_server = 127.0.0.1:22122

目前版本还支持了基于.properties的配置文件。前缀以fastdfs.开头,支持多个tracker_server通过逗号分隔的形式,可根据需要采取不同的文件类型。

配置文件设置了连接的超时时间、编码格式、访问资源端口以及tracker_server地址等信息。

工具类封装

文件类封装

完成了依赖引入和配置文件引入,则需对文件信息进行简单的封装,封装为类FastDFSFile:

public class FastDFSFile {

    private String name;
    private byte[] content;
    private String ext;
    private String md5;
    private String author;
    // 省略getter/setter
}

该类主要用于存储和传输文件相关信息。

初始化

针对上面的配置文件,封装 FastDFSClient类来进度初始化读取、文件的上传、下载、删除等方法。

先来看初始化部分:

/**
 * 配置文件名称
 */
private static final String CONF_FILE = "fastdfs.conf";

private static TrackerClient trackerClient;
private static TrackerServer trackerServer;
private static StorageServer storageServer;
private static StorageClient1 storageClient;

static {
    try {
        ClientGlobal.init(CONF_FILE);
        trackerClient = new TrackerClient();
        trackerServer = trackerClient.getTrackerServer();
        // 如果因为测试环境通过docker不会,无法正确获取storageServer的地址信息,因此进行初始化配置,可采用此方式指定ip,不建议在生产使用
        // storageServer = new StorageServer("127.0.0.1", 23000, 0);

        storageServer = null;
        storageClient = new StorageClient1(trackerServer, storageServer);
        System.out.println("初始化配置信息:" + ClientGlobal.configInfo());
    } catch (Exception e) {
        logger.error("初始化FastDFSClient异常", e);
    }
}

这里通过ClientGlobal的init方法对配置文件进行加载,然后便可以直接创建TrackerClient、TrackerServer、StorageServer、StorageClient1等对象。

上传方法

通过调用StorageClient的uploadFile便可进行上传。

以下核心方法如果使用官方依赖jar包,方法名为upload_file这种格式,其他方法也类似,可对应调整。

public static String[] upload(FastDFSFile file) {

    NameValuePair[] metaList = new NameValuePair[1];
    metaList[0] = new NameValuePair("author", file.getAuthor());

    String[] uploadResults = null;
    try {
        uploadResults = storageClient.uploadFile(file.getContent(), file.getExt(), metaList);
    } catch (Exception e) {
        logger.error("上传文件异常,文件名:" + file.getName(), e);
    }

    if (uploadResults == null && storageClient != null) {
        logger.error("上传文件异常,错误码:" + storageClient.getErrorCode());
    }

    return uploadResults;
}

NameValuePair主要存储文件的一些基础属性,如作者信息、创建时间等,这些信息在上传之后会独立生成一个同名但类型不同的文件,用来存储。可以直接在storage的目录下看到里面的内容。

获取文件

直接调用StorageClient的getFileInfo方法便可获取对应的文件信息。

public static FileInfo getFile(String groupName, String remoteFileName) {
    try {
        return storageClient.getFileInfo(groupName, remoteFileName);
    } catch (Exception e) {
        logger.error("获取文件异常", e);
    }
    return null;
}

下载文件

通过用StorageClient的downloadFile方法进行下载。

public static InputStream downFile(String groupName, String remoteFileName) {
    try {
        byte[] fileByte = storageClient.downloadFile(groupName, remoteFileName);
        return new ByteArrayInputStream(fileByte);
    } catch (Exception e) {
        logger.error("下载文件异常", e);
    }
    return null;
}

删除文件

根据分组和名称删除文件。

public static void deleteFile(String groupName, String remoteFileName)
            throws Exception {
    storageClient.deleteFile(groupName, remoteFileName);
}

其他相关方法就不注意举例,可直接调用StorageClient对应的方法进行操作即可。

控制层相关代码

控制层核心代码,上传并进行页面跳转:

@PostMapping("/upload")
public String singleFileUpload(@RequestParam("file") MultipartFile file,RedirectAttributes redirectAttributes) {

    try {
        String path = saveFile(file);
        redirectAttributes.addFlashAttribute("message", "上传文件成功");
        redirectAttributes.addFlashAttribute("path", "文件访问URL:'" + path + "'");
    } catch (Exception e) {
        logger.error("upload file failed", e);
    }
    return "redirect:/result";
}

其中saveFile为上传文件操作:

private String saveFile(MultipartFile multipartFile) throws IOException {
    String fileName = multipartFile.getOriginalFilename();
    assert fileName != null;
    String ext = fileName.substring(fileName.lastIndexOf(".") + 1);
    FastDFSFile file = new FastDFSFile(fileName, multipartFile.getBytes(), ext);
    String[] fileAbsolutePath = {};
    try {
        fileAbsolutePath = FastDFSClient.upload(file);
    } catch (Exception e) {
        logger.error("上传文件异常", e);
    }
    if (fileAbsolutePath == null) {
        logger.error("上传失败,请重新再试");
        // TODO 此处根据具体情况抛出自定义业务异常
        throw new RuntimeException("上传失败");
    }
    return FastDFSClient.getTrackerUrl() + fileAbsolutePath[0] + "/" + fileAbsolutePath[1];
}

主要是根据上传的文件获取对应的文件信息,然后调用FastDFSClient的upload方法。

启动项目,通过页面操作,上传完成,获得对应的访问链接。

image

对应的本地文件中也存储了上传的文件和对应的额外信息。

image

同时,通过返回的url地址可以在浏览器上正常访问到图片的信息。

小结

本篇文章的完整源码访问如下链接:https://github.com/secbr/springboot-all/tree/master/springboot-fastdfs

关于FastDFS相关系列文章就到这里。同时为大家整理了一整套关于FastDFS相关的资料,关注公众号:程序新视界,回复“011”获得完整版PDF文档。



Spring Boot集成FastDFS实现文件上传插图(2)

关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台

除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接

本文链接:http://www.choupangxia.com/2020/07/30/spring-boot-fastdfs/