• docker下安装fastdfs,并使用java操作api


    关于fastdfs

    一、Docker安装FastDFS

    关于Docker的安装不同的操作系统有不同的安装方法,这里就不再赘述,默认你的电脑或服务器上已经安装好了Docker,并已启动。

    我们来看一下Docker库里面已经存在了哪些镜像可供使用。执行以下命令(如果非管理员权限则可能需要使用sudo):

    bogon:~ zzs$ docker search fastdfs
    NAME                           DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
    season/fastdfs                 FastDFS                                         66
    luhuiguo/fastdfs               FastDFS is an open source high performance d…   25                                      [OK]
    ygqygq2/fastdfs-nginx          整合了nginx的fastdfs                                20                                      [OK]
    morunchang/fastdfs             A FastDFS image                                 19
    delron/fastdfs                                                                 12
    moocu/fastdfs                  fastdfs5.11                                     9
    qbanxiaoli/fastdfs             FastDFS+FastDHT单机版                              8                                       [OK]
    ……
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • pull 下来
    docker pull delron/fastdfs
    
    • 1

    二、构建Tracker容器

    使用docker镜像构建tracker容器,用于启动跟踪服务器,起到调度的作用。

    docker run -d --network=host --name tracker -v /Users/zzs/develop/temp/tracker:/var/fdfs delron/fastdfs tracker
    
    • 1

    上面的启动命令是在Linux下,如果是Mac或Windows操作系统network=host(容器与主机享受相同的network namespace)会失效,此时需要指定对应的端口映射。本教程的Docker宿主机为Mac。

    docker run -d --name tracker -p 22122:22122 -v /Users/zzs/develop/temp/tracker:/var/fdfs delron/fastdfs tracker
    
    • 1

    默认tracker是监听在22122端口的。

    其中-v参数将本地的目录与容器中的/var/fdfs目录进行挂载。

    这里可以看到tracker服务已经启动完成。

    docker ps
    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                                     NAMES
    15bfca8b94eb        delron/fastdfs      "/usr/bin/start1.sh …"   4 seconds ago       Up 3 seconds        8080/tcp, 8888/tcp, 23000/tcp, 0.0.0.0:22122->22122/tcp   tracker
    
    • 1
    • 2
    • 3

    三、构建Storage容器

    使用docker镜像构建storage容器,用于启动存储服务器,提供容量和备份服务。

    在执行下面命令时特别需要提醒的时,对应的IP地址,需要修改为tracker服务的IP地址,由于是在同一台电脑上操作,这里使用本机的内网地址即可,22122是tracker对应的端口。

    示例,下面命令需要替换IP地址。

    docker run -d --network=host --name storage -e TRACKER_SERVER=ip:22122 -v /Users/zzs/develop/temp/storage:/var/fdfs -e GROUP_NAME=group1 delron/fastdfs storage
    
    • 1

    替换IP地址之后对应Mac下的的具体执行操作:

    docker run -d --name storage -p 8888:8888 -p 23000:23000 -e TRACKER_SERVER=192.168.43.143:22122 -v /Users/zzs/develop/temp/storage:/var/fdfs -e GROUP_NAME=group1 delron/fastdfs storage
    
    • 1

    其中8888为Nginx对应的访问端口,23000是storage服务端口。

    • 一定要注意:如果GROUP_NAME的值是自己自定义的,例如:user等等,这就相当于新建group,那么要修改storage、Tracker两个服务下的/etc/fdfs/mod_fastdfs.conf 如下:
      在这里插入图片描述
    • 再修改storage、Tracker两个服务下的/usr/local/nginx/conf/nginx.conf如下:然后重启这两个服务。
      在这里插入图片描述
      参考文章

    此时可查看docker的服务情况:

    docker ps
    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                                                   NAMES
    2bc9f8268eda        delron/fastdfs      "/usr/bin/start1.sh …"   5 seconds ago       Up 4 seconds        8080/tcp, 0.0.0.0:8888->8888/tcp, 22122/tcp, 0.0.0.0:23000->23000/tcp   storage
    15bfca8b94eb        delron/fastdfs      "/usr/bin/start1.sh …"   2 minutes ago       Up 2 minutes        8080/tcp, 8888/tcp, 23000/tcp, 0.0.0.0:22122->22122/tcp                 tracker
    
    • 1
    • 2
    • 3
    • 4

    四、修改配置

    经过上面的步骤,tracker和storage都启动完成。我们可以进入对应的docker容器查看一下默认的配置情况。

    进入docker的命令为:

    docker exec -it 2bc9f8268eda bash
    
    • 1

    其中参数值“2bc9f8268eda”为我们上面看到的要进入的容器的CONTAINER ID。

    先进入storage,查看其对应配置文件中关于http访问的配置,配置文件在/etc/fdfs目录下的storage.conf。在最后一行可以看到如下配置:

    # the port of the web server on this storage server
    http.server_port=8888
    
    • 1
    • 2

    也就是说,这个docker镜像中默认监听的是8888端口,当然此配置是需要修改的。如果修改为其他端口,对应的Nginx配置也需要修改。

    那么Nginx配置在哪里呢?也在当前容器当中。Nginx配置文件的根目录为:

    /usr/local/nginx/conf/
    
    • 1

    可以对其下的nginx.conf进行查看和修改。先来看一下默认配置:

    server {
        listen       8888;
        server_name  localhost;
        location ~/group[0-9]/ {
            ngx_fastdfs_module;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root html;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    上面是关于server的配置,端口与storage的端口一一对应。如果需要修改时两个同步修改。

    通过上面的操作你会发现默认FastDFS会需要开启8888、23000、22122端口,如果在Linux操作系统下,注意防火墙对对应端口的开放。

    这里经过我多次踩坑,建议用docker 的host网络方式启动这两服务,然后storage的TRACKER_SERVER写宿主机的真实ip地址

    五、测试一下

    经过上面的步骤已经完成了FastDFS的安装和配置,下面我们来放一张图片验证一下。

    首先,将一张图片放在本机挂载目录/Users/zzs/develop/temp/storage下(上面命令中用到的目录)。

    然后,进入storage容器,进入/var/fdfs目录,执行如下命令:

    /usr/bin/fdfs_upload_file /etc/fdfs/client.conf weixin.jpg
    
    • 1

    其中wenxin.jpg是前面存放在本机storage目录下的图片的名称。

    相关执行命令及目录:

    [root@2bc9f8268eda fdfs]# pwd
    /var/fdfs
    [root@2bc9f8268eda fdfs]# /usr/bin/fdfs_upload_file /etc/fdfs/client.conf weixin.jpg
    group1/M00/00/00/rBEAA18X7ZWAfAPiAABrsFVlX6U142.jpg
    
    • 1
    • 2
    • 3
    • 4

    此时,文件已经上传成功,会返回在storage存储文件的路径信息。通过url访问http://ip:8888/group1/M00/00/00/rBEAA18X7ZWAfAPiAABrsFVlX6U142.jpg,即可查看到图片。这里ip替换成localhost或127.0.0.1接口。

    六、JavaApi 操作

    1. 导入pom坐标
     <!-- https://mvnrepository.com/artifact/org.csource/fastdfs-client-java -->
            <dependency>
                <groupId>org.csource</groupId>
                <artifactId>fastdfs-client-java</artifactId>
                <version>1.28-SNAPSHOT</version>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    如有报错,参考这篇文章

    1. 配置文件
    connect_timeout = 8
    network_timeout = 30
    charset = UTF-8
    http.anti_steal_token = no
    # 追踪器访问文件端口号
    http.tracker_http_port=8888
    storage.user.group=user
    tracker_server = 192.168.3.250:22122
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. FastDFSClient
    package com.demo.fasfdfs;
    
    import org.apache.commons.lang3.StringUtils;
    import org.csource.common.MyException;
    import org.csource.common.NameValuePair;
    import org.csource.fastdfs.*;
    
    import java.io.*;
    
    /**
      * FastDFS 分布式文件系统 Java 客户端工具类
      * 具体功能:文件上传、下载、替换、删除、查询文件元数据、查看文件详情
      */
    public class FastDFSClient {
    
                // 获取配置文件地址
                private static final String CONF_FILENAME = "D:\\OneDrive\\学习\\fastdfs\\fasfdfs\\src\\main\\resources\\my.conf";
                // Storage 存储服务器客户端
                private static StorageClient storageClient = null;
    
                static {
                    try {
                        // 加载配置文件
                        ClientGlobal.init(CONF_FILENAME);
                        // 初始化 Tracker 客户端
                        TrackerClient trackerClient = new TrackerClient(ClientGlobal.g_tracker_group);
                        // 初始化 Tracker 服务端
                        TrackerServer trackerServer = trackerClient.getTrackerServer();
                        System.out.println(trackerServer);
                        System.out.println(trackerClient);
    
    
                        // 初始化 Storage 服务端
                        StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
                        System.out.println(storageServer);
                        // 初始化 Storage 客户端
                        storageClient = new StorageClient(trackerServer, storageServer);
    
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (MyException e) {
                        e.printStackTrace();
                    }
                }
    
                /**
          * 文件上传
          *
          * @param inputStream 上传的文件的字节输入流
          * @param fileName    上传的文件的原始名
          * @return
          */
                public static String[] uploadFile(InputStream inputStream, String fileName) {
                    try {
                        // 准备字节数组
                        byte[] fileBuff = null;
                        // 文件元数据
                        NameValuePair[] metaList = null;
                        if (inputStream != null) {
                            // 查看文件的长度
                            int len = inputStream.available();
                            // 初始化元数据数组
                            metaList = new NameValuePair[2];
                            // 第一组元数据,文件的原始名称
                            metaList[0] = new NameValuePair("file_name", fileName);
                            // 第二组元数据,文件的长度
                            metaList[1] = new NameValuePair("file_length", String.valueOf(len));
                            // 创建对应长度的字节数组
                            fileBuff = new byte[len];
                            // 将输入流中的字节内容,读到字节数组中
                            inputStream.read(fileBuff);
                        }
                        /*
                    上传文件。
                    参数含义:要上传的文件的内容(使用字节数组传递),上传的文件的类型(扩展名),元数据
                 */
                        String[] fileids = storageClient.upload_file(fileBuff, getFileExt(fileName), metaList);
                        return fileids;
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (MyException e) {
                        e.printStackTrace();
                    }
                    return null;
                }
    
                /**
          * 文件上传
          *
          * @param file     上传的文件
          * @param fileName 上传的文件的原始名
          * @return
          */
                public static String[] uploadFile(File file, String fileName) {
                    try (FileInputStream fis = new FileInputStream(file)) {
                        return uploadFile(fis, fileName);
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    return null;
                }
    
                /**
          * 获取文件后缀名(不带点)
          *
          * @param fileName
          * @return 如:"jpg" or ""
          */
                private static String getFileExt(String fileName) {
                    if (StringUtils.isBlank(fileName) || !fileName.contains(".")) {
                        return "";
                    }
                    return fileName.substring(fileName.lastIndexOf(".") + 1); // 不带最后的点
                }
    
                /**
          * 获取文件详情
          *
          * @param groupName      组/卷名,默认值:group1
          * @param remoteFileName 文件名,例如:"M00/00/00/wKgKZl9tkTCAJAanAADhaCZ_RF0495.jpg"
          * @return 文件详情
          */
                public static FileInfo getFileInfo(String groupName, String remoteFileName) {
                    try {
                        return storageClient.get_file_info(groupName == null ? "group1" : groupName, remoteFileName);
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (MyException e) {
                        e.printStackTrace();
                    }
                    return null;
                }
    
                /**
          * 获取元数据
          *
          * @param groupName      组/卷名,默认值:group1
          * @param remoteFileName 文件名,例如:"M00/00/00/wKgKZl9tkTCAJAanAADhaCZ_RF0495.jpg"
          * @return 文件的元数据数组
          */
                public static NameValuePair[] getMetaData(String groupName, String remoteFileName) {
                    try {
                        // 根据组名和文件名通过 Storage 客户端获取文件的元数据数组
                        return storageClient.get_metadata(groupName == null ? "group1" : groupName, remoteFileName);
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (MyException e) {
                        e.printStackTrace();
                    }
                    return null;
                }
    
                /**
          * 文件下载
          *
          * @param groupName      组/卷名,默认值:group1
          * @param remoteFileName 文件名,例如:"M00/00/00/wKgKZl9tkTCAJAanAADhaCZ_RF0495.jpg"
          * @return 文件的字节输入流
          */
                public static InputStream downloadFile(String groupName, String remoteFileName) {
                    try {
                        // 根据组名和文件名通过 Storage 客户端获取文件的字节数组
                        byte[] bytes = storageClient.download_file(groupName == null ? "group1" : groupName, remoteFileName);
                        // 返回字节流对象
                        InputStream inputStream = new ByteArrayInputStream(bytes);
                        return inputStream;
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (MyException e) {
                        e.printStackTrace();
                    }
                    return null;
                }
    
                /**
          * 文件删除
          *
          * @param groupName      组/卷名,默认值:group1
          * @param remoteFileName 文件名,例如:"M00/00/00/wKgKZl9tkTCAJAanAADhaCZ_RF0495.jpg"
          * @return 0为成功,非0为失败
          */
                public static int deleteFile(String groupName, String remoteFileName) {
                    int result = -1;
                    try {
                        // 根据组名和文件名通过 Storage 客户端删除文件
                        result = storageClient.delete_file(groupName == null ? "group1" : groupName, remoteFileName);
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (MyException e) {
                        e.printStackTrace();
                    }
                    return result;
                }
    
                /**
          * 修改一个已经存在的文件
          *
          * @param oldGroupName 旧组名
          * @param oldFileName  旧文件名
          * @param file         新文件
          * @param fileName     新文件名
          * @return
          */
                public static String[] modifyFile(String oldGroupName, String oldFileName, File file, String fileName) {
                    // 先上传
                    String[] fileids = uploadFile(file, fileName);
                    if (fileids == null) {
                        return null;
                    }
                    // 再删除
                    int delResult = deleteFile(oldGroupName, oldFileName);
                    if (delResult != 0) {
                        return null;
                    }
                    return fileids;
                }
            }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    1. 测试类
    public class Test {
        public static void main(String[] args) {
            String[] fileids = FastDFSClient.uploadFile(new File("D:\\OneDrive\\Desktop\\4.png"), "china.jpg");
            for (String fileid : fileids) {
                System.out.println("fileid = " + fileid);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    参考文章

  • 相关阅读:
    glide set gif start stop
    SOC_AHB_SD_IF
    一种基于时空位置预测的空间众包任务分配方法
    深入理解Kafka核心设计与实战原理 笔记
    嵌入式串口转CAN模块详细参数分析
    asp毕业设计——基于asp+access的在线考试系统设计与实现(毕业论文+程序源码)——在线考试系统
    Unity DOTS学习 前置知识(二)
    精品基于Uniapp+SSM实现的Android平台的健康管理系统
    989. 数组形式的整数加法
    【数据结构】二叉树
  • 原文地址:https://blog.csdn.net/cristianoxm/article/details/127399283