我愛java系列之---【分布式文件存儲-FastDFS】


一、體系結構 

二、上傳流程

什么是fastDFS:
fastDFS是一個底層使用C語言編寫的, 開源的免費的分布式文件系統
fastDFS作用:
fastDFS主要作用就是上傳文件, 下載文件, 刪除文件等對文件的管理存儲.

fastDFS運行原理:
1. fastDFS分為三部分: 客戶端, 是指我們的購物項目。
          tracker端, 也就是管理端是指管理服務器, 管理服務器不存儲文件, 只負責管理存儲端服務器。
          storage端, 也就是存儲端, 負責存儲具體文件內容。
2. 存儲端啟動后會到管理端注冊, 告訴管理端他的ip, 端口和狀態, 我們需要存儲文件的時候, 連接管理端,管理端會給我們返回具體服務器的ip地址, 端口號, 客戶端就拿着ip和端口去調用存儲端服務器存儲文件,存儲后會返回文件存儲的路徑和文件名, 存儲端服務器會對文件自動重命名, 防止文件重名。
3. 管理端: 一台主機, 多台備機, 主備之間有心跳檢測機制, 可以高可用,管理端有負載均衡的功能, 可以將請求均勻的分配給每一台存儲端服務器處理。
 存儲端: 一台主機, 一台備機, 主備之間有心跳檢測機制, 高可用, 存儲的時候, 向主機中存儲內容。
    主機會將內容同步到備機, 主備之間存儲的內容一樣, 叫做冗余備份功能, 容災效果強,存儲端服務器可以理論上無限擴容, 擴展性強。
優點:
1. 管理端有高可用, 負載均衡功能, 可以承載高並發
2. 存儲端冗余備份, 高可用, 無限擴容
缺點:
服務器集群需要企業自行搭建運維, 成本比較高.
使用場景:
適合大型互聯網公司, 大規模存儲任務使用.

fastDFS硬件服務器:
fastDFS服務器叫做存儲服務器: 要求讀寫效率高
1. 如果經費充足: 可以購買ibm的nas服務器
2. 如果經費不夠充足: 一般可以使用8塊硬盤組成raid10磁盤陣列系統
硬盤分類: 5400轉
7200轉
12000轉 讀最快500多兆每秒, 寫最快300多兆每秒

三、FastDFS搭建

 

我們使用Docker搭建FastDFS的開發環境

 

拉取鏡像

docker pull morunchang/fastdfs

運行tracker

docker run -d --name tracker --net=host morunchang/fastdfs sh tracker.sh

運行storage

docker run -d --name storage --net=host -e TRACKER_IP=<your tracker server address>:22122 -e GROUP_NAME=<group name> morunchang/fastdfs sh storage.sh
  • 使用的網絡模式是–net=host, <your tracker server address> 替換為你機器的Ip即可

  • <group name> 是組名,即storage的組

  • 如果想要增加新的storage服務器,再次運行該命令,注意更換 新組名

修改nginx的配置

進入storage的容器內部,修改nginx.conf

docker exec -it storage  /bin/bash

進入后

vi /data/nginx/conf/nginx.conf

添加以下內容

location /group1/M00 {
   proxy_next_upstream http_502 http_504 error timeout invalid_header;
     proxy_cache http-cache;
     proxy_cache_valid  200 304 12h;
     proxy_cache_key $uri$is_args$args;
     proxy_pass http://fdfs_group1;
     expires 30d;
 }

退出容器

exit

重啟storage容器

docker restart storage

四、文件存儲微服務

 

創建文件管理微服務changgou_service_file,該工程主要用於實現文件上傳以及文件刪除等功能。

 

(1)修改pom.xml,引入依賴

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>changgou_service</artifactId>
        <groupId>com.changgou</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>changgou_service_file</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>net.oschina.zcx7878</groupId>
            <artifactId>fastdfs-client-java</artifactId>
            <version>1.27.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.changgou</groupId>
            <artifactId>changgou_common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

(2)在resources文件夾下創建fasfDFS的配置文件fdfs_client.conf

connect_timeout = 60
network_timeout = 60
charset = UTF-8
http.tracker_http_port = 8080
tracker_server = 192.168.200.128:22122

connect_timeout:連接超時時間,單位為秒。

network_timeout:通信超時時間,單位為秒。發送或接收數據時。假設在超時時間后還不能發送或接收數據,則本次網絡通信失敗

charset: 字符集

http.tracker_http_port :.tracker的http端口

tracker_server: tracker服務器IP和端口設置

(3)在resources文件夾下創建application.yml

spring:
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 10MB
server:
  port: 9008
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:6868/eureka
  instance:
    prefer-ip-address: true
feign:
  hystrix:
    enabled: true

max-file-size是單個文件大小,max-request-size是設置總上傳的數據大小

(4)啟動類 創建com.changgou包,創建啟動類FileApplication

@SpringBootApplication
@EnableEurekaClient
public class FileApplication {

    public static void main(String[] args) {
        SpringApplication.run(FileApplication.class);
    }
}

五、文件上傳

 

1.文件信息封裝

 

文件上傳一般都有文件的名字、文件的內容、文件的擴展名、文件的md5值、文件的作者等相關屬性,我們可以創建一個對象封裝這些屬性,代碼如下:

 

創建com.changgou.file.pojo.FastDFSFile

public class FastDFSFile {
    //文件名字
    private String name;
    //文件內容
    private byte[] content;
    //文件擴展名
    private String ext;
    //文件MD5摘要值
    private String md5;
    //文件創建作者
    private String author;

    public FastDFSFile(String name, byte[] content, String ext, String height,
                       String width, String author) {
        super();
        this.name = name;
        this.content = content;
        this.ext = ext;
        this.author = author;
    }

    public FastDFSFile(String name, byte[] content, String ext) {
        super();
        this.name = name;
        this.content = content;
        this.ext = ext;
    }

    // getter and setter ...
}

2.文件操作

 

創建FastDFSClient類,放在com.itheima.file.util下在該類中實現FastDFS信息獲取以及文件的相關操作,

 

代碼如下:

public class FastDFSClient {
    
    private static org.slf4j.Logger logger = LoggerFactory.getLogger(FastDFSClient.class);

    /***
     * 初始化加載FastDFS的TrackerServer配置
     */
    static {
        try {
            String filePath = new ClassPathResource("fdfs_client.conf").getFile().getAbsolutePath();
            ClientGlobal.init(filePath);
        } catch (Exception e) {
            logger.error("FastDFS Client Init Fail!",e);
        }
    }

    /***
     * 文件上傳
     * @param file
     * @return
     */
    public static String[] upload(FastDFSFile file) {
        //獲取文件的作者
        NameValuePair[] meta_list = new NameValuePair[1];
        meta_list[0] = new NameValuePair("author", file.getAuthor());

        //接收返回數據
        String[] uploadResults = null;
        StorageClient storageClient=null;
        try {
            //創建StorageClient客戶端對象
            storageClient = getTrackerClient();

            /***
             * 文件上傳
             * 1)文件字節數組
             * 2)文件擴展名
             * 3)文件作者
             */
            uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), meta_list);
        } catch (Exception e) {
            logger.error("Exception when uploadind the file:" + file.getName(), e);
        }

        if (uploadResults == null && storageClient!=null) {
            logger.error("upload file fail, error code:" + storageClient.getErrorCode());
        }
        //獲取組名
        String groupName = uploadResults[0];
        //獲取文件存儲路徑
        String remoteFileName = uploadResults[1];
        return uploadResults;
    }

    /***
     * 獲取文件信息
     * @param groupName:組名
     * @param remoteFileName:文件存儲完整名
     * @return
     */
    public static FileInfo getFile(String groupName, String remoteFileName) {
        try {
            StorageClient storageClient = getTrackerClient();
            return storageClient.get_file_info(groupName, remoteFileName);
        } catch (Exception e) {
            logger.error("Exception: Get File from Fast DFS failed", e);
        }
        return null;
    }

    /***
     * 文件下載
     * @param groupName
     * @param remoteFileName
     * @return
     */
    public static InputStream downFile(String groupName, String remoteFileName) {
        try {
            //創建StorageClient
            StorageClient storageClient = getTrackerClient();

            //下載文件
            byte[] fileByte = storageClient.download_file(groupName, remoteFileName);
            InputStream ins = new ByteArrayInputStream(fileByte);
            return ins;
        } catch (Exception e) {
            logger.error("Exception: Get File from Fast DFS failed", e);
        }
        return null;
    }

    /***
     * 文件刪除
     * @param groupName
     * @param remoteFileName
     * @throws Exception
     */
    public static void deleteFile(String groupName, String remoteFileName)
            throws Exception {
        //創建StorageClient
        StorageClient storageClient = getTrackerClient();

        //刪除文件
        int i = storageClient.delete_file(groupName, remoteFileName);
    }

    /***
     * 獲取Storage組
     * @param groupName
     * @return
     * @throws IOException
     */
    public static StorageServer[] getStoreStorages(String groupName)
            throws IOException {
        //創建TrackerClient
        TrackerClient trackerClient = new TrackerClient();
        //獲取TrackerServer
        TrackerServer trackerServer = trackerClient.getConnection();
        //獲取Storage組
        return trackerClient.getStoreStorages(trackerServer, groupName);
    }

    /***
     * 獲取Storage信息,IP和端口
     * @param groupName
     * @param remoteFileName
     * @return
     * @throws IOException
     */
    public static ServerInfo[] getFetchStorages(String groupName,
                                                String remoteFileName) throws IOException {
        TrackerClient trackerClient = new TrackerClient();
        TrackerServer trackerServer = trackerClient.getConnection();
        return trackerClient.getFetchStorages(trackerServer, groupName, remoteFileName);
    }

    /***
     * 獲取Tracker服務地址
     * @return
     * @throws IOException
     */
    public static String getTrackerUrl() throws IOException {
        return "http://"+getTrackerServer().getInetSocketAddress().getHostString()+":"+ClientGlobal.getG_tracker_http_port()+"/";
    }

    /***
     * 獲取Storage客戶端
     * @return
     * @throws IOException
     */
    private static StorageClient getTrackerClient() throws IOException {
        TrackerServer trackerServer = getTrackerServer();
        StorageClient storageClient = new StorageClient(trackerServer, null);
        return  storageClient;
    }

    /***
     * 獲取Tracker
     * @return
     * @throws IOException
     */
    private static TrackerServer getTrackerServer() throws IOException {
        TrackerClient trackerClient = new TrackerClient();
        TrackerServer trackerServer = trackerClient.getConnection();
        return  trackerServer;
    }
}

3.文件上傳

創建一個FileController,在該控制器中實現文件上傳操作,代碼如下:

@RestController
@CrossOrigin
public class FileController {

    @PostMapping("/upload")
    public String upload(@RequestParam("file") MultipartFile file) {
        String path ="";
        try {
            path=saveFile(file);
            System.out.println(path);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return path;
    }

    /**
     * @param multipartFile
     * @return
     * @throws IOException
     */
    public String saveFile(MultipartFile multipartFile) throws IOException {
        //1. 獲取文件名
        String fileName = multipartFile.getOriginalFilename();
        //2. 獲取文件內容
        byte[] content = multipartFile.getBytes();
        //3. 獲取文件擴展名
        String ext = "";
        if (fileName != null && !"".equals(fileName)) {
            ext = fileName.substring(fileName.lastIndexOf("."));
        }
        //4. 創建文件實體類對象
        FastDFSFile fastDFSFile = new FastDFSFile(fileName, content, ext);
        //5. 上傳
        String[] uploadResults = FastDFSClient.upload(fastDFSFile);
        //6. 拼接上傳后的文件的完整路徑和名字, uploadResults[0]為組名, uploadResults[1]為文件名稱和路徑
        String path = FastDFSClient.getTrackerUrl() + uploadResults[0] + "/" + uploadResults[1];
        //7. 返回
        return path;
    }
}

六、Postman測試文件上傳

步驟:

1、選擇post請求方式,輸入請求地址 http://localhost:9007/upload

2、填寫Headers

Key:Content-Type
Value:multipart/form-data

3、填寫body

選擇form-data 然后選擇文件file 點擊添加文件,最后發送即可。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM