一、简介
FastDFS是一个轻量级的开源分布式文件系统。主要解决了大容量文件存储和高并发访问的问题,文件存取时实现了负载均衡。实现了软件方式的磁盘阵列,可以使用廉价的IDE硬盘进行存储,并且支持存储服务器在线扩容。支持相同内容的文件只保存一份,节约磁盘空间。
FastDFS只能通过Client API访问,不支持POSIX访问方式。
FastDFS特别适合大中型网站使用,用来存储资源文件,如:图片、文档、音频、视频等。
磁盘阵列:技术是加州大学伯克利分校1987年提出,最初是为了组合小的廉价磁盘来代替大的昂贵磁盘,同时希望磁盘失效时不会使对数据的访问受损 失而开发出一定水平的数据保护技术。RAID就是一种由多块廉价磁盘构成的冗余阵列,在操作系统下是作为一个独立的大型存储设备出现。RAID可以充分发 挥出多块硬盘的优势,可以提升硬盘速度,增大容量,提供容错功能够确保数据安全性,易于管理的优点,在任何一块硬盘出现问题的情况下都可以继续工作,不会 受到损坏硬盘的影响RAID(Redundant Array of Independent Disk 独立冗余磁盘阵列)技术是加州大学伯克利分校1987年提出,最初是为了组合小的廉价磁盘来代替大的昂贵磁盘,同时希望磁盘失效时不会使对数据的访问受损 失而开发出一定水平的数据保护技术。RAID就是一种由多块廉价磁盘构成的冗余阵列,在操作系统下是作为一个独立的大型存储设备出现。RAID可以充分发 挥出多块硬盘的优势,可以提升硬盘速度,增大容量,提供容错功能够确保数据安全性,易于管理的优点,在任何一块硬盘出现问题的情况下都可以继续工作,不会 受到损坏硬盘的影响
下载网址:FastDFS没有官网。但是作者余庆(happy_fish100)担任chinaunix中FastDFS板块版主。并且会不定期更新板块中内容。http://bbs.chinaunix.net/
FastDFS软件可以在sourceforge中进行下载,最新版本为5.08
https://sourceforge.net/projects/fastdfs/files/
二、FastDFS架构(面试题)
1、架构图
2、角色
client:客户端,使用java语言编写的项目属于客户端。
Tracker Server:跟踪服务器,主要做调度工作,在访问上起负载均衡的作用。在内存中记录集群中group和storage server状态信息,是连接Client和Storage和Storage server的枢纽。
Storage Server:存储服务器,文件和文件属性(meta data)都保存到存储服务器中。
3、架构解读
- 只有两个角色,tracker server和storage server,不需要存储文件索引信息。
 - 所有服务器都是对等的,不存在Master-Slave关系。
 - 存储服务器采用分组方式,同组内存储服务器上的文件完全相同(RAID 1).
 - 不同组的Storage Server之间不会相互通信。
 - 由Storage server主动向tracker server报告状态信息,tracker server之间不会相互通信。
三、FastDFS安装
1、拉取镜像
docker中fastdfs没有官方镜像docker pull delron/fastdfs
2、创建并启动tracker容器
—network表示与宿主共用网络docker run -d --network=host --name tracker -v /var/fdfs/tracker:/var/fdfs delron/fastdfs tracker
3、创建并启动storage容器
注意:第一次启动storage是成功的,但是如果希望重新启动成功,必须要先删除Linux中docker run -d --network=host --name storage -e TRACKER_SERVER=192.168.80.128:22122 -v /var/fdfs/storage:/var/fdfs -e GROUP_NAME=group1 delron/fastdfs storage
/var/fdfs/storage/data/fdfs_.pid
然后再启动。否则查看docker容器日志会出现如下的情况。
四、操作文件流程
1、文件上传
1.1 时序图
1.2 流程说明
 
- 客户端访问Tacker
 - Tracker返回Storage的IP和端口号
 - 客户端直接访问Storage,把文件内容和元数据发送过去。
 - Storage返回文件存储id。包含组名和文件名。
 
2、文件下载
2.1 时序图
2.2 流程说明
- client询问tracker下载的文件的storage,参数为文件标识(组名和文件名);
 - tracker返回一台可用的storage
 - 
2.3 注意事项
 直接使用工具方法完成下载即可
- 使用java原生API实现输入流和输出流的转换
 - downloadFile的第二个参数要写成自己上传后的图片路径。
3、文件删除
3.1 注意事项
 
添加依赖
<dependencies><dependency><groupId>cn.bestwu</groupId><artifactId>fastdfs-client-java</artifactId><version>1.27</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.4</version></dependency></dependencies>
编写配置文件
- 在resources下新建fastdfs的配置文件
 - 文件名:fdfs_client.conf
 - 修改成自己的tracker服务器ip
connect_timeout = 10network_timeout = 30charset = UTF-8http.tracker_http_port = 8080tracker_server = 192.168.80.128:22122
 
- 导入工具类
 
在com.bjsxt.utils.FastDFSClient下创建工具类。
如果新建的是聚合的项目,完成FastDFS文件上传操作需要修改成:
private static final String CONF_FILENAME = “fdfs_client.conf”;
FastDFSClient工具类文件(点击即可预览或下载):FastDFSClient.java
新建测试类,完成上传、下载、删除、修改的测试
package com.bjsxt.utils;import java.io.*;import java.util.Arrays;public class TestA {public static void main(String[] args) {//up();//down();//remove();modify();}//文件上传实现//注意事项:参数A:需要上传的文件// 参数B是文件的类型 文件名称无所谓但是必须保证文件类型一致,因为FastDFS需要对文件进行重命名//String[] strings = FastDFSClient.uploadFile(new File("D:/1.png"), "xxxxx.png");//[group1, M00/00/00/wKgIgWGEoK2APyjKAAE8sEFdJRc658.png]// System.out.println(Arrays.toString(strings));/*文件的上传*/public static void up(){File file = new File("E:/aa.png");String[] result = FastDFSClient.uploadFile(file, "xxxx.png");System.out.println("length:"+result.length);System.out.println(result[0]);System.out.println(result[1]);}/*文件的下载*/public static void down(){try {InputStream is = FastDFSClient.downloadFile("group1", "M00/00/00/wKhQgGGE2-iAFuIOAAiDton9kwo378.png");FileOutputStream os = new FileOutputStream(new File("E:/jqk.png"));int i=0;while ((i=is.read())!=-1){os.write(i);}os.flush();os.close();} catch (IOException e) {e.printStackTrace();}}//文件删除 :注意 删除成功返回值是0/*文件的删除*/public static void remove(){int index = FastDFSClient.deleteFile("group1", "M00/00/00/wKhQgGGE3s-Aam5eAAiDton9kwo634.png");System.out.println("result:"+index);}/*文件的修改*/public static void modify(){String[] group1s = FastDFSClient.modifyFile("group1", "M00/00/00/wKhQgGGE2-iAFuIOAAiDton9kwo378.png", new File("E:/aa.png"), "xx.png");System.out.println(Arrays.toString(group1s));}}
六、Nginx
1、简介
FastDFS是没有HTTP协议文件访问功能的,需要借助其他工具实现图片HTTP访问的。Nginx就具备了代理虚拟机主机功能。
Nginx是一个高性能的HTTP和反向代理服务。它具有很多非常优越的特性:在连接高并发的情况下,Nginx是Apache服务不错的替代品。2、代理方式-正向代理
正向代理:一个位于客户端和原始服务器之间的服务器,为了从原始服务器中获取内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端,客户端才能使用正向代理。
3、代理方式-反向代理
反向代理(Reverse Proxy)方式是值以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。
4、正反向代理区别
正向代理即是客户端代理, 代理客户端, 服务端不知道实际发起请求的客户端.
反向代理即是服务端代理, 代理服务端, 客户端不知道实际提供服务的服务端
5、Nginx作用
5.1 HTTP协议代理(反向代理)
只要支持HTTP协议访问的内容,都可以由Nginx进行代理。Nginx只支持HTTP协议的代理,其他协议不支持。
5.2 搭建虚拟主机(暴露主机端口,外部可以通过http访问)
Nginx可以监听所安装的主机的某个端口,对外支持这个端口的HTTP访问。当接收到外部HTTP请求后把本机中资源返回给客户端。今天的课程内容就是使用Nginx的搭建虚拟主机功能,外部请求图片时,把图片信息响应给请求发。
5.3 负载均衡(反向代理)
6、Nginx安装配置
修改Nginx配置
进入到storage容器后修改nginx配置文件
| docker exec -it storage /bin/bash vi /tmp/nginx/nginx-1.12.2/conf/nginx.conf  | 
|---|
在配置文件的http里面添加下面内容
| server{ listen 8888; server_name localhost; location ~/group([0-9])/M00{ ngx_fastdfs_module; } }  | 
|---|
- 重启storage容器
 
docker stop storage
删除 xxx.pid文件
docker start storage
- 测试
 
所有操作必须在storage容器内部进行操作。
保证在容器命令行中,任意目录下执行命令。
向a.html尾加bjsxt,nihao。如果a.html不存在会新建这个文件
| echo “bjsxt,nihao” > a.html | 
|---|
使用fastdfs客户端命令进行上传文件。
fdfs_upload_file 客户端工具,client.conf 客户端配置文件 a.html要上传的文件
| /usr/bin/fdfs_upload_file /etc/fdfs/client.conf a.html | 
|---|
执行完成后会输出可访问访问路径。
在浏览器中访问,如果正常访问,说明安装成功。
七、案例
application.yml
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/nginx?serverTimezone=Asia/Shanghai&useSSL=false&characterEncoding=utf8username: rootpassword: root#设置文件上传的大小servlet:multipart:max-file-size: 10MBmax-request-size: 10MBmybatis:#起别名type-aliases-package: com.bjsxt.pojo#扫描mybatis下所有的xml文件mapper-locations: classpath:mybatis/*.xml
FastDFSClient工具类文件(点击即可预览或下载):FastDFSClient.java
fdfs_client.conf
connect_timeout = 10network_timeout = 30charset = UTF-8http.tracker_http_port = 8080tracker_server = 192.168.80.128:22122
mappr和service示例
public interface StudentMapper {int insert(Student student);@Select("select * from student")List<Student> selectMore();@Delete("delete from student where sid=#{sid}")int delectOne(Student student);}
public interface StudentService {int insertStu(Student student, MultipartFile mpf);List<Student> findAll();int delOne(Student student);}
```java @Service @Transactional public class StudentServiceImpl implements StudentService { @Autowired private StudentMapper studentMapper;
/删除/ @Override public int delOne(Student student) {
int flag=0;try {int index = FastDFSClient.deleteFile(student.getGroupname(), student.getFilename());System.out.println(student.getGroupname());System.out.println(student.getFilename());System.out.println("index:"+index);studentMapper.delectOne(student);flag=1;} catch (Exception e) {throw e;}return flag;
}
/查找/ @Override public List
findAll() { return studentMapper.selectMore();}
/添加/ @Override public int insertStu(Student student , MultipartFile mpf) {
try { String[] strings = FastDFSClient.uploadFile(mpf.getInputStream(), mpf.getOriginalFilename()); student.setGroupname(strings[0]); student.setFilename(strings[1]); student.setUrl("http://192.168.80.128:8888/"+strings[0]+"/"+strings[1]); student.setFiletype(mpf.getContentType()); } catch (IOException e) { e.printStackTrace(); } int insert = studentMapper.insert(student); return insert;}
}
5. StudentController
```java
@Controller
public class StudentController {
    @Autowired
    private StudentService studentService;
    @RequestMapping("/{path}")
    public String getPath(String path){
        return path;
    }
    @RequestMapping("/favicon.ico")
    @ResponseBody
    public void favicon(){
    }
    @PostMapping("/add")
    public String add(Student student, MultipartFile mpf){
        System.out.println(student);
        System.out.println(mpf);
        int i = studentService.insertStu(student, mpf);
        if (i>0){
            return "redirect:/list";
        }
            return "forward:/insert";
    }
    @RequestMapping("/list")
    /*model作用域等同于request*/
    public String list(Model model){
        List<Student> list = studentService.findAll();
        model.addAttribute("list",list);
        return "show";
    }
    @RequestMapping("/download")
    public void download(String groupname, String filename, String filetype, HttpServletResponse response) throws IOException {
        //告诉浏览器把下载的文件保存到硬盘上 不在浏览器进行解析
        response.setHeader("Content-Disposition", "attachment;filename="+filename);
        //设置下载文件的类型
        response.setContentType(filetype);
        InputStream inputStream = FastDFSClient.downloadFile(groupname, filename);
        //下载到客户端本地
        ServletOutputStream outputStream = response.getOutputStream();
        IOUtils.copy(inputStream,outputStream);
        outputStream.close();
        inputStream.close();
    }
    @RequestMapping("/remove")
        public String remove(Student student){
            int i = studentService.delOne(student);
            if (i>0){
                return "redirect:/list";
            }
            return "redirect:/delerr";
        }
}
- 前台页面
 


