分布式文件存储系统--fastdfs

为什么需要文件存储系统

回想传统项目中,文件存储的方式是上传到tomcat服务器中,经过分布式、集群的学习,大家眼界提升,知道tomcat等服务器就用来跑java项目,我们还会觉得它扛不住压力,而采用tomcat集群。居然还用来存储文件,显然不太合适。而且tomcat能存储多少文件?
针对中大型项目,需要一种专业文件服务器来完成对文件存储、访问等功能

主流的文件存储一般两种方式

  1. 采用第三方的文件服务器,比如七牛云等,简单方便,不用自己搭建,但是贵,而且文件在别人手中
  2. 自己搭建一套服务器,比如fastdfs、hbase等。

什么是fastdfs

百度百科:

Fastdfs:存储、访问、上传、下载文件

1.2fastdfs详细介绍
前面已经介绍过,项目大了,将以前的单系统拆分,如图:
在这里插入图片描述

问题:这么大的系统,一台服务器hold得住吗?所以,如图:
在这里插入图片描述

然后fastdfs给文件服务器改名为Storage Server:存储服务器
问题:因为一台storage存的数据量有限,所以用了上图的多台,每台存储的数据不相同,如果其中一台挂了,所存储的部分数据就会丢失,所以,如图:
在这里插入图片描述

问题:这么多组,现在的大家很容易就能想到负载均衡,所以如图,复杂的图直接借了:
在这里插入图片描述

Tracker server:跟踪服务器,主要做调度工作,起负载均衡的作用。在内存中记录集群中所有存储组和存储服务器的状态信息,是客户端和数据服务器交互的枢纽

每个storage在启动后会连接Tracker,告知自己所属的group等信息,并保持周期性的心跳,tracker根据storage的心跳信息,建立group==>[storage server list]的映射表。

理解了上面的关系,整个文件上传流程就很简单了:
在这里插入图片描述

流程:首先每个组存储服务器storage会向tracker定时上传状态信息,比如剩余空间,是否可用等,客户端(java、php等)上传文件时,先请求tracker,tracker根据storage的情况,将某一台storage的ip、端口等告知client,client调用API开始上传,storage存储好了后,将文件存储的具体位置形成相对路径file_id返回给客户端,客户端存储文件路径到mysql等

  1. tracker有多个,客户端如何选择:采用nginx来完成负载均衡
  2. group有多个tracker如何选择(了解)
    通过参数store_lookup = 0 配置
    0:所有的Group以轮询方式进行选择
    1:指定Group,该Group名称由store_group配置指定
    2:负载均衡,表示选择空余容量最大的Group
  3. 每个group中很多个服务器如何选择(了解)
    通过参数store_server = 0 配置
    0:Group中的所有Storage进行轮询
    1:选择IP地址最大的Storage
    2:根据优先级配置(在每个storage.conf、upload_priority配置)
  4. 选择了某台服务器,如何选择服务器中的磁盘及目录(了解)
    通过参数store_path = 0配置
    0:对所有的Store_path进行轮询
    1:负载均衡,选择空闲空间最大的Store_path

好像有点麻烦,其实这些你都不用管,主要是通过上面可用总结出,storage存储文件,返回file_id应该包含,group信息,磁盘信息,目录信息,文件名信息(具体机器不用返回,每台都会同步),例如如图:

在这里插入图片描述

文件下载,网上的图,不用解释了,有点小问题就是 client 在发送下载请求给某个 tracker,必须带上文件名信息,tracker 从文件名中解析出文件的 group、大小、创建时间等信息,然后再返回合适的storage的ip与端口
在这里插入图片描述

1.3fastdfs环境搭建(自行百度)

Java配置fastdfs文件上传下载

需要的文件在附件中
1.4springboot整合fastdfs
1.添加依赖

<dependency>
    <groupId>com.github.tobato</groupId>
    <artifactId>fastdfs-client</artifactId>
    <version>1.26.2</version>
</dependency>

2.配置文件

分布式文件系统FDFS配置

#读取数据超时时间
fdfs.soTimeout=1500
#链接超时时间
fdfs.connectTimeout=600
#缩略图宽高  压缩后的
fdfs.thumbImage.width=150
fdfs.thumbImage.height=150
#跟踪调度器 集群就加在后面就可用了
fdfs.trackerList[0]=172.18.34.9:22122
#fdfs.trackerList[1]=192.168.0.202:22122
package com.xmcc.redis01.fastdfs;

import com.github.tobato.fastdfs.FdfsClientConfig;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableMBeanExport;
import org.springframework.context.annotation.Import;
import org.springframework.jmx.support.RegistrationPolicy;

/**
 * 导入FastDFS-Client组件
 */
@Configuration
//表示将FdfsClientConfig类 的对象交给spring管理 @Import非常简单 需要掌握的注解 自己看
@Import(FdfsClientConfig.class)
// 解决jmx重复注册bean的问题  jmx是一个监控与管理的框架 以后有机会在去学习吧
@EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)
public class FastDFSConfig {
   
}

测试:

package com.xmcc.redis01;

import com.github.tobato.fastdfs.domain.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class Redis01ApplicationTests {

   @Autowired
   private FastFileStorageClient fastFileStorageClient;

        @Test
    public void upload() throws FileNotFoundException {
        File file = new File("E://timg.jpg");
        FileInputStream fileInputStream = new FileInputStream(file);
        //文件输入流    文件长度   文件格式  第四个不管
        StorePath storePath = fastFileStorageClient.uploadFile(fileInputStream, file.length(), "jpg", null);

        log.info("文件上传返回路径为:{}",storePath.getFullPath());
    }

}


效果:
 
OK的
 

那么以后上传文件只需要保存这个返回的路径,因为是nginx放在前端就可以访问了

工具类:
package com.xmcc.redis01.fastdfs;

import com.github.tobato.fastdfs.domain.StorePath;
import com.github.tobato.fastdfs.exception.FdfsUnsupportStorePathException;
import com.github.tobato.fastdfs.proto.storage.DownloadByteArray;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.Charset;

@Component
@Slf4j
public class FastDFSUtils {
    @Autowired
    private FastFileStorageClient storageClient;


    /**
     * 上传文件
     *
     * @param file 文件对象
     * @return 文件访问地址
     * @throws IOException
     */
    public String uploadFile(MultipartFile file) throws IOException {
        StorePath storePath = storageClient.uploadFile(file.getInputStream(), file.getSize(), FilenameUtils.getExtension(file.getOriginalFilename()), null);
        return getAccessUrl(storePath);
    }

    /**
     * 普通文件上传
     * @param file
     * @return
     * @throws IOException
     */
    public String uploadFile(File file) throws IOException {
        FileInputStream fileInputStream = new FileInputStream(file);
        //获取后缀名
        String fileName = file.getName();
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
        StorePath storePath = storageClient.uploadFile(fileInputStream, file.length(), suffix, null);
        return getAccessUrl(storePath);
    }

    /**
     * 将一段字符串生成一个文件上传
     *
     * @param content       文件内容
     * @param fileExtension
     * @return
     */
    public String uploadFile(String content, String fileExtension) {
        byte[] buff = content.getBytes(Charset.forName("UTF-8"));
        ByteArrayInputStream stream = new ByteArrayInputStream(buff);
        StorePath storePath = storageClient.uploadFile(stream, buff.length, fileExtension, null);
        return getAccessUrl(storePath);
    }

    private String getAccessUrl(StorePath storePath) {
        String fileUrl = storePath.getFullPath();
        return fileUrl;
    }

    /**
     * 删除文件
     *
     * @param fileUrl 文件访问地址
     * @return
     */
    public void deleteFile(String fileUrl) {
        if (StringUtils.isEmpty(fileUrl)) {
            return;
        }
        try {
            StorePath storePath = StorePath.praseFromUrl(fileUrl);
            storageClient.deleteFile(storePath.getGroup(), storePath.getPath());
        } catch (FdfsUnsupportStorePathException e) {
            log.warn(e.getMessage());
        }
    }

    /**
     * 文件名下载文件
     * @param fileUrl
     * @return
     * @throws IOException
     */
    public byte[] downloadFile(String fileUrl) throws IOException {
        String group = fileUrl.substring(0, fileUrl.indexOf("/"));
        String path = fileUrl.substring(fileUrl.indexOf("/") + 1);
        DownloadByteArray downloadByteArray = new DownloadByteArray();
        byte[] bytes = storageClient.downloadFile(group, path, downloadByteArray);
        return bytes;
    }


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值