FastDFS分布式文件系统环境搭建和代码测试
FastDFS分布式文件系统环境搭建和代码测试
待配置的机器:
192.168.44.10 192.168.44.11 192.168.44.12 192.168.44.13
注意:关闭linux防火墙,或者开放FastDFS所需的一些列端口
一、安装必要的工具和依赖
1、工具
yum install lrzsz wget vim unzip net-tools -y
2、依赖
yum install gcc perl openssl openssl-devel pcre pcre-devel zlib zlib-devel libevent libevent-devel -y
二、安装FastDFS
1、安装包下载地址
码云地址:https://gitee.com/fastdfs100
下载FastDFS、libfastcommon、fastdfs-nginx-module(可选)、fastdfs-client-java(可选)
使用rz -y 上传文件,使用sz下载文件,这个工具在lrzsz安装,有这个工具后,用拖拽也可以。
2、安装libfastcommon 
libfastcommon 库是 FastDFS 文件系统运行需要的公共 C 语言函数库
解压:tar -zxvf fastdfs100-libfastcommon-V1.0.51.tar.gz
如果是zip包使用:unzip fastdfs100-libfastcommon-V1.0.51.zip
进入目录,编译:./make.sh
编译没报错可以安装:./make.sh install
3、安装FastDFS
解压:tar -zxvf fastdfs100-fastdfs-V6.07.tar.gz
或 unzip fastdfs100-fastdfs-V6.07.zip
进入目录,编译:./make.sh
编译没报错可以安装:./make.sh install
所有编译出来的文件存放在/usr/bin****目录下
所有配置文件存放在/etc/fdfs****目录下
通过ls /usr/bin/fdfs* 可以查看所有fastDFS的命令
三、配置FastDFS
1、将/etc/fdfs下的配置文件样例全部改名,去掉sample后缀
 mv client.conf.sample client.conf
 mv storage.conf.sample storage.conf
 mv storage_ids.conf.sample storage_ids.conf
 mv tracker.conf.sample tracker.conf
2、将安装目录conf下的http.conf和拷贝到/etc/fdfs下
cp http.conf /etc/fdfs/
cp mime.types /etc/fdfs/
3、分组
group1:192.168.44.10 192.168.44.11
group2: 192.168.44.12 192.168.44.13
tracker server: 192.168.44.10 192.168.44.12
storage server: 全部机器
group1和group2的数据在各自的组内自动复制,做冗余备份。
group1和group2之间数据不重复,只为提高并发和容量。
4、创建日志目录和数据目录
mkdir -p /opt/fastdfs/tracker #tracker的日志目录
mkdir -p /opt/fastdfs/storage #storage的日志目录
mkdir -p /opt/fastdfs/storage/files #真正存放文件的目录
5、修改tracker.conf
192.168.44.10
base_path = /opt/fastdfs/tracker
store_group = group1
192.168.44.12
base_path = /opt/fastdfs/tracker
store_group = group2
store_group参数与store_lookup参数相关
注意:FastDFS默认是带有负载均衡策略的可以在tracker的2台机器中修改tracker.conf文件
store_lookup=10 随机存放策略
1 指定组
2 选择磁盘空间的优先存放 默认值修改后重启服务
fdfs_trackerd /etc/fdfs/tracker.conf restart
6、修改storage.conf
# group1或group2,看分组配置
group_name = group1 
base_path = /opt/fastdfs/storage
store_path0 = /opt/fastdfs/storage/files
tracker_server = 192.168.44.10:22122
tracker_server = 192.168.44.12:22122
7、启动tracker server
192.168.44.10和192.168.44.12
fdfs_trackerd /etc/fdfs/tracker.conf start
ps -ef | grep fdfs
root       1753      1  0 11:30 ?        00:00:00 fdfs_trackerd /etc/fdfs/tracker.conf
8、启动storage server
fdfs_storaged /etc/fdfs/storage.conf start
需要通过ps -ef | grep fdfs检查是否启动。
/opt/fastdfs/storage/files/data 这个目录下是数据存放位置,data目录是FastDFS自动创建,这个目录下有00~FF个目录
9、查看storage注册情况
fdfs_monitor /etc/fdfs/storage.conf
10、重启FastDFS
重启tracker
fdfs_trackerd /etc/fdfs/tracker.conf restart
重启storage
fdfs_storaged /etc/fdfs/storage.conf restart
11、关闭FastDFS
fdfs_trackerd /etc/fdfs/tracker.conf stop
fdfs_storaged /etc/fdfs/storage.conf stop
不建议在生产环境使用 kill -9 强制关闭,因为可能会导致文件信息不同步问题
四、环境测试
使用FastDFS自带的测试工具
1、修改client.conf配置文件,主要修改两个配置:
base_path=/opt/fastdfs/client
tracker_server=192.168.44.10:22122
2、测试
执行上传命令
fdfs_test /etc/fdfs/client.conf upload /root/test.txt
删除测试
fdfs_delete_file /etc/fdfs/client.conf group1/要删除的文件路径
注意:
没有搭建集群默认只有一个组group1
后缀名包含-m的为属性文件(meta)
五、代码测试
使用Springboot进行代码测试
https://gitee.com/asker124143222/my-boot.git
1、依赖
        
            com.github.tobato 
            fastdfs-client 
            1.27.2 
         
2、service
property外置前缀upload到application.yml里
@ConfigurationProperties(prefix = "upload")
@Data
public class UploadProperties {
    private String baseUrl;
    private List allowTypes;
}
 
UploadService
package com.home.system.service;
import com.github.tobato.fastdfs.domain.fdfs.FileInfo;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.home.system.config.UploadProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashSet;
/**
 * @Author: xu.dm
 * @Date: 2021/6/16 14:44
 * @Version: 1.0
 * @Description: TODO
 **/
@Slf4j
@EnableConfigurationProperties(UploadProperties.class)
@Service
public class UploadService {
    @Resource
    private FastFileStorageClient storageClient;
    @Resource
    private UploadProperties properties;
    public String uploadImage(MultipartFile file) {
        // 1、校验文件类型
        String contentType = file.getContentType();
        if (!properties.getAllowTypes().contains(contentType)) {
            throw new RuntimeException("文件类型不支持");
        }
        // 2、校验文件内容
        try {
            BufferedImage image = ImageIO.read(file.getInputStream());
            if (image == null || image.getWidth() == 0 || image.getHeight() == 0) {
                throw new RuntimeException("上传文件有问题");
            }
        } catch (IOException e) {
            log.error("校验文件内容失败....{}", e);
            throw new RuntimeException("校验文件内容失败" + e.getMessage());
        }
        try {
            // 3、上传到FastDFS
            // 3.1、获取扩展名
            String extension = StringUtils.substringAfterLast(file.getOriginalFilename(), ".");
            // 3.2、上传
            StorePath storePath = storageClient.uploadFile(file.getInputStream(), file.getSize(), extension, new HashSet<>());
            System.out.println(storePath.getGroup());
            System.out.println(storePath.getPath());
            System.out.println(storePath);
            // 返回路径
            return properties.getBaseUrl() + storePath.getFullPath();
        } catch (IOException e) {
            log.error("【文件上传】上传文件失败!...." + e);
            throw new RuntimeException("【文件上传】上传文件失败!" + e.getMessage());
        }
    }
    public String uploadFile(MultipartFile file) {
        try {
            // 3、上传到FastDFS
            // 3.1、获取扩展名
            String extension = StringUtils.substringAfterLast(file.getOriginalFilename(), ".");
            // 3.2、上传
            StorePath storePath = storageClient.uploadFile(file.getInputStream(), file.getSize(), extension, new HashSet<>());
            System.out.println(storePath.getGroup());
            System.out.println(storePath.getPath());
            System.out.println(storePath);
            // 返回路径
            return properties.getBaseUrl() + storePath.getFullPath();
        } catch (IOException e) {
            log.error("【文件上传】上传文件失败!...." + e);
            throw new RuntimeException("【文件上传】上传文件失败!" + e.getMessage());
        }
    }
    public void downloadFile(HttpServletResponse response, String groupName, String path) {
        FileInfo fileInfo = null;
        try {
            fileInfo = storageClient.queryFileInfo(groupName, path);
        }catch (Exception e){
            throw new RuntimeException("文件不存在");
        }
        if(fileInfo == null) {
            throw new RuntimeException("文件不存在");
        }
        InputStream inputStream = storageClient.downloadFile(groupName, path, ins -> ins);
        String filename = path.replace("/", ".");
        response.setContentType("application/octet-stream;charset=UTF-8");
        response.setHeader("Content-Disposition", "attachment; filename="+filename);
        try {
            streamCopy(inputStream,response.getOutputStream());
            response.flushBuffer();
        } catch (IOException e) {
            closeQuietly(inputStream);
            e.printStackTrace();
            throw new RuntimeException("下载文件失败!");
        }
    }
    public void deleteFile(String groupName, String path) {
        storageClient.deleteFile(groupName, path);
    }
    private void streamCopy(InputStream inp, OutputStream out) throws IOException {
        byte[] buff = new byte[4096];
        int count;
        while ((count = inp.read(buff)) != -1) {
            if (count > 0) {
                out.write(buff, 0, count);
            }
        }
    }
    public static void closeQuietly( final Closeable closeable ) {
        // no need to log a NullPointerException here
        if(closeable == null) {
            return;
        }
        try {
            closeable.close();
        } catch ( Exception e ) {
            log.error( "Unable to close resource: " + e,
                    e );
        }
    }
}
3、controller
package com.home.system.controller;
import com.home.system.service.UploadService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
 * @Author: xu.dm
 * @Date: 2021/6/16 14:50
 * @Version: 1.0
 * @Description: TODO
 **/
@Controller
public class UploadController {
    @Resource
    private UploadService uploadService;
    @GetMapping("/upload")
    public String upload() {
        return "upload";
    }
    @GetMapping("/fileDelete")
    public String fileDelete() {
        return "fileDelete";
    }
    @GetMapping("/fileDownload")
    public String fileDownload() {
        return "download";
    }
    @PostMapping("file/download")
    public void doDownload(HttpServletResponse response, String groupName, String path){
        uploadService.downloadFile(response,  groupName, path);
    }
    @PostMapping("file/delete")
    @ResponseBody
    public String doDelete(String groupName, String path){
        uploadService.deleteFile(groupName, path);
        return "删除成功";
    }
    @RequestMapping("upload/doUpload")
    @ResponseBody
    public Map doUpload(MultipartFile file){
        System.out.println(file.getOriginalFilename());
        Map upload =new HashMap<>();
        String path = this.uploadService.uploadImage(file);
        upload.put("path",path);
        return upload;
    }
    @RequestMapping("upload/doFileUpload")
    @ResponseBody
    public Map doFileUpload(MultipartFile file){
        System.out.println(file.getOriginalFilename());
        Map upload =new HashMap<>();
        String path = this.uploadService.uploadFile(file);
        upload.put("path",path);
        return upload;
    }
}
    
4、application.yml文件配置
server:
  port: 8080
  tomcat:
    accept-count: 3
    max-connections: 6
    threads:
      max: 10
    basedir: ./target/
logging:
  file:
    # 一旦日志文件大于设定值,这个日志文件就会被加上日期归档,原文清空重新开始记录日志
    name: ./target/myBoot.log
  logback:
    rollingpolicy:
      max-file-size: 20MB
      max-history: 30
spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
  servlet:
    multipart:
      max-request-size: 500MB
      max-file-size: 500MB
fdfs:
  connect-timeout: 60000
  so-timeout: 120000
  tracker-list:
    - 192.168.44.10:22122
    - 192.168.44.12:22122
upload:
  base-url: http://192.168.1.105/ #换成你的ip
  allow-types:
    - image/jpeg
    - image/png
    - image/bmp