一、简介
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 = 10
network_timeout = 30
charset = UTF-8
http.tracker_http_port = 8080
tracker_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.Driver
url: jdbc:mysql://localhost:3306/nginx?serverTimezone=Asia/Shanghai&useSSL=false&characterEncoding=utf8
username: root
password: root
#设置文件上传的大小
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
mybatis:
#起别名
type-aliases-package: com.bjsxt.pojo
#扫描mybatis下所有的xml文件
mapper-locations: classpath:mybatis/*.xml
FastDFSClient工具类文件(点击即可预览或下载):FastDFSClient.java
fdfs_client.conf
connect_timeout = 10
network_timeout = 30
charset = UTF-8
http.tracker_http_port = 8080
tracker_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";
}
}
- 前台页面