文章目录
1.什么是FastDFS
FastDFS是由淘宝的余庆先生所开发的一个开源的分布式文件系统。用纯C语言开发,功能丰富:
- 文件存储
- 文件同步
- 文件访问(上传、下载)
- 存取负载均衡
- 在线扩容
适合有大容量存储需求的应用或系统。同类的分布式文件系统有谷歌的GFS、HDFS(Hadoop)、TFS(淘宝)等。
2.FastDFS的架构
2.1.架构图
先上图:
FastDFS两个主要的角色:Tracker Server 和 Storage Server 。
-
Tracker:跟踪服务器,管理集群,tracker也可以实现集群。每个tracker节点地位平等。收集Storage集群的状态。
-
Storage:存储服务器,实际保存文件,分为多个组,每个组之间保存的文件是不同的。每个组内部可以有多个成员,组成员内部保存的内容是一样的,组成员的地位是一致的,没有主从的概念。
2.2.上传和下载流程
上传
- Client通过Tracker server查找可用的Storage server。
- Tracker server向Client返回一台可用的Storage server的IP地址和端口号。
- Client直接通过Tracker server返回的IP地址和端口与其中一台Storage server建立连接并进行文件上传。
- 上传完成,Storage server返回Client一个文件ID(卷、目录),文件上传结束。
下载
- Client通过Tracker server查找要下载文件所在的的Storage server。
- Tracker server向Client返回包含指定文件的某个Storage server的IP地址和端口号。
- Client直接通过Tracker server返回的IP地址和端口与其中一台Storage server建立连接并指定要下载文件。
- 下载文件成功。
3.安装和使用
参考资料的:
4.java客户端
余庆先生提供了一个Java客户端,但是作为一个C程序员,写的java代码可想而知。而且已经很久不维护了。
这里推荐一个开源的FastDFS客户端,支持SpringBoot。
配置使用极为简单,支持连接池,支持自动生成缩略图,狂拽酷炫吊炸天啊,有木有。
接下来,我们就用FastDFS改造power_shop_item工程。
4.1.引入依赖
在父工程中,添加管理依赖,版本为:
<fastDFS-client-version>1.26.2</fastDFS-client-version>
<!--fastdfs-->
<dependency>
<groupId>com.github.tobato</groupId>
<artifactId>fastdfs-client</artifactId>
<version>${fastDFS-client-version}</version>
</dependency>
因此,这里我们直接在power_shop_item工程的pom.xml中引入坐标即可:
<dependency>
<groupId>com.github.tobato</groupId>
<artifactId>fastdfs-client</artifactId>
</dependency>
4.2.引入配置类
纯java配置:
@Configuration
//只需要一行注解 @Import(FdfsClientConfig.class)就可以拥有带有连接池的FastDFS Java客户端了
@Import(FdfsClientConfig.class)
public class FastClientImporter {
}
4.3.编写FastDFS属性
在application.yml配置文件中追加如下内容:
fdfs:
so-timeout: 1501 # 超时时间
connect-timeout: 601 # 连接超时时间
thumb-image: # 缩略图
width: 60
height: 60
tracker-list: # tracker地址:你的虚拟机服务器地址+端口(默认是22122)
- 192.168.116.133:22122
4.4.配置hosts
将来通过域名:image.powershop.com这个域名访问fastDFS服务器上的图片资源。所以,需要代理到虚拟机地址:http://image.powershop.com/group1/M00/00/00/wKjMhV7Du3WATIbZAABIvlHWjnY123.png
- 配置hosts文件,使image.powershop.com可以访问fastDFS服务器
#测试环境
192.168.204.158 image.powershop.com
4.5.测试
创建测试类:
import com.github.tobato.fastdfs.domain.StorePath;
import com.github.tobato.fastdfs.domain.ThumbImageConfig;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
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.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes={PowerShopItemApp.class})
public class FastDFSTest {
@Autowired
private FastFileStorageClient storageClient;
@Autowired
private ThumbImageConfig thumbImageConfig;
@Test
public void testUpload() throws FileNotFoundException {
// 要上传的文件
File file = new File("D:/images/1.jpg");
// 上传并保存图片,参数:1-上传的文件流 2-文件的大小 3-文件的后缀 4-可以不管他
StorePath storePath = this.storageClient.uploadFile(
new FileInputStream(file), file.length(), "jpg", null);
// 带分组的路径
System.out.println(storePath.getFullPath());
// 不带分组的路径
System.out.println(storePath.getPath());
}
@Test
public void testUploadAndCreateThumb() throws FileNotFoundException {
File file = new File("D:/images/1.jpg");
// 上传并且生成缩略图
StorePath storePath = this.storageClient.uploadImageAndCrtThumbImage(
new FileInputStream(file), file.length(), "png", null);
// 带分组的路径
System.out.println(storePath.getFullPath());
// 不带分组的路径
System.out.println(storePath.getPath());
// 获取缩略图路径
String path = thumbImageConfig.getThumbImagePath(storePath.getFullPath());
System.out.println(path);
}
}
结果:
group1:卷名
/M00:store_path0 目录
00/00:两层256个文件夹
group1/M00/00/00/wKg4ZVsWl5eAdLNZAABAhya2V0c424.jpg
M00/00/00/wKg4ZVsWl5eAdLNZAABAhya2V0c424.jpg
group1/M00/00/00/wKjpgV6XBT2Abi8rAACaMWefTPs531.png
M00/00/00/wKjpgV6XBT2Abi8rAACaMWefTPs531.png
group1/M00/00/00/wKjpgV6XBT2Abi8rAACaMWefTPs531_60x60.png
访问第二组第一个路径:
访问最后一个路径(缩略图路径):
4.6.改造上传逻辑
import com.github.tobato.fastdfs.domain.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.bjpowershop.utils.Result;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/**
* 图片上传
*/
@RestController
@RequestMapping("/file")
public class FileUploadController {
@Autowired
private FastFileStorageClient storageClient;
private static final List<String> CONTENT_TYPES =
Arrays.asList("image/jpeg", "image/gif");
/**
* 图片上传
*/
@RequestMapping("/upload")
public Result fileUpload(MultipartFile file) {
try {
String originalFilename = file.getOriginalFilename();
// 校验文件的类型
String contentType = file.getContentType();
if (!CONTENT_TYPES.contains(contentType)){
// 文件类型不合法,直接返回
return Result.error("文件类型不合法:"+originalFilename);
}
// 校验文件的内容
BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
if (bufferedImage == null) {
return Result.error("文件内容不合法:" + originalFilename);
}
// 保存到服务器
//file.transferTo(new File("D:\\images\\" + originalFilename));
String ext = StringUtils.substringAfterLast(originalFilename, ".");
StorePath storePath = this.storageClient.uploadFile(file.getInputStream(),
file.getSize(), ext, null);
// 生成url地址,返回
return Result.ok("http://image.powershop.com/" + storePath.getFullPath());
} catch (IOException e) {
e.printStackTrace();
}
return Result.error("服务器内部错误");
}
}
只需要把原来保存文件的逻辑去掉,然后上传到FastDFS即可。
4.5.测试页面上传
上传成功:
查询列表显示图片地址: