一. 基本概念
Fastdfs是用C语言编写的一款开源的分布式文件系统。Fastdfs为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用 Fastdfs很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
Fastdfs架构包括Tracker server和Storage server。客户端请求Tracker server 进行文件上传、下载,通过 Tracker server 调度,最终由Storage server完成文件上传和下载。
Tracker server作用是负载均衡和调度,通过Tracker server在文件上传时可以根据一些策略找到Storage server 提供文件上传服务。可以将 tracker 称为追踪服务器或调度服务器。
二. Fastdfs系统结构
Storage作用是文件存储,客户端上传的文件最终存储在Storage服务器上。
服务端两个角色:
Tracker:作用是负载均衡和调度
Storage:作用是文件存储,客户端上传的文件最终存储在 Storage 服务器上
客户端上传文件后存储服务器将文件 ID 返回给客户端,此文件 ID 用于以后访问该文件的索引信息。文件索引信息包括:组名,虚拟磁盘路径,数据两级目录,文件名。如:/group1/M00/01/B2/CgAIC2Md0nWAKjOCAAQRef-xdIY168.jpg
组名/卷:文件上传后所在的Storage组名称,在文件上传成功后有Storage服务器返回,需要客户端自行保存
虚拟磁盘路径:Storage配置的虚拟路径,与磁盘选项 store_path*对应。如果配置了store_path0 则是 M00,如果配置了 store_path1 则是 M01,以此类推
- 数据两级目录:Storage服务器在每个虚拟磁盘路径下创建的两级目录,用于存储数据文件
- 文件名:与文件上传时不同。是由存储服务器根据特定信息生成,文件名包含:源存储服务器 IP 地址、文件创建时间戳、文件大小、随机数和文件拓展名等信息
1 2 3 4 5
| 0. storage会定期向tracker上传当前状态【心跳机制】 1. 客户端发送请求,请求tracker【调度中心】 2. tracker选择哪一个storage能使用,并且返回能用的storage 3. 客户端把上传的附件,上传到storage中去 4. storage会向客户端返回上传文件的路径
|
三. 使用FastDfs系统
导入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13
| <dependency> <groupId>cn.bestwu</groupId> <artifactId>fastdfs-client-java</artifactId> <version>1.27</version> </dependency>
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.8.0</version> </dependency>
|
添加配置文件fdfs_client.conf 到资源文件夹
1 2 3
| tracker_server=123.207.27.208:22122
|
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| ClientGlobal.init("D:/maven_work/fastDFS-demo/src/fdfs_client.conf");
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getConnection();
StorageServer storageServer = null;
StorageClient storageClient = new StorageClient(trackerServer, storageServer);
String[] strings = storageClient.upload_file("D:/pic/benchi.jpg", "jpg",null);
for (String string : strings) { System.out.println(string); }
|
访问上传图片
1
| http://123.207.27.208/group1/M00/01/B3/CgAIC2Md3KGAGa0xAAALg4Ak6_M929.jpg
|
四. FastDfsUtils工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
| package io.coderyeah.basic.util;
import org.csource.fastdfs.*;
public class FastDfsUtils {
public static String CONF_FILENAME = FastDfsUtils.class.getClassLoader().getResource("fdfs_client.conf").getFile();
public static String upload(byte[] file, String extName) {
try { ClientGlobal.init(CONF_FILENAME);
TrackerClient tracker = new TrackerClient(); TrackerServer trackerServer = tracker.getConnection(); StorageServer storageServer = null;
StorageClient storageClient = new StorageClient(trackerServer, storageServer);
String fileIds[] = storageClient.upload_file(file, extName, null);
System.out.println(fileIds.length); System.out.println("组名:" + fileIds[0]); System.out.println("路径: " + fileIds[1]); return "/" + fileIds[0] + "/" + fileIds[1]; } catch (Exception e) { e.printStackTrace(); return null; } }
public static String upload(String path, String extName) {
try { ClientGlobal.init(CONF_FILENAME);
TrackerClient tracker = new TrackerClient(); TrackerServer trackerServer = tracker.getConnection(); StorageServer storageServer = null; StorageClient storageClient = new StorageClient(trackerServer, storageServer); String fileIds[] = storageClient.upload_file(path, extName, null);
System.out.println(fileIds.length); System.out.println("组名:" + fileIds[0]); System.out.println("路径: " + fileIds[1]); return "/" + fileIds[0] + "/" + fileIds[1];
} catch (Exception e) { e.printStackTrace(); return null; } }
public static byte[] download(String groupName, String fileName) { try {
ClientGlobal.init(CONF_FILENAME);
TrackerClient tracker = new TrackerClient(); TrackerServer trackerServer = tracker.getConnection(); StorageServer storageServer = null;
StorageClient storageClient = new StorageClient(trackerServer, storageServer); byte[] b = storageClient.download_file(groupName, fileName); return b; } catch (Exception e) { e.printStackTrace(); return null; } }
public static void delete(String groupName, String fileName) { try { ClientGlobal.init(CONF_FILENAME);
TrackerClient tracker = new TrackerClient(); TrackerServer trackerServer = tracker.getConnection(); StorageServer storageServer = null;
StorageClient storageClient = new StorageClient(trackerServer, storageServer); int i = storageClient.delete_file(groupName, fileName); System.out.println(i == 0 ? "删除成功" : "删除失败:" + i); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("删除异常," + e.getMessage()); } }
}
|
五. 文件上传接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| package io.coderyeah.basic.controller;
import io.coderyeah.basic.Result; import io.coderyeah.basic.util.FastDfsUtils; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
@Api(value = "文件的上传和下载接口", tags = "fastDfs文件上传") @RestController @RequestMapping("/fastDfs") public class FastDfsController {
@ApiOperation("通过fastDfs文件上传到服务器") @PostMapping() public Result upload(@RequestPart(value = "file", required = true) MultipartFile file) { final String originalFilename = file.getOriginalFilename(); String suffix = originalFilename.substring(originalFilename.lastIndexOf(".") + 1); try { final String filepath = FastDfsUtils.upload(file.getBytes(), suffix); return Result.success(filepath,null); } catch (IOException e) { e.printStackTrace(); return Result.fail("上传失败!" + e.getMessage()); } }
@ApiOperation("删除文件") @DeleteMapping() public Result delete(@RequestParam("path") String path) { try { final String tempPath = path.substring(1); final String groupName = tempPath.substring(0, tempPath.indexOf("/")); System.out.println(tempPath); System.out.println(groupName); final String remotePath = tempPath.substring(tempPath.indexOf("/") + 1); FastDfsUtils.delete(groupName, remotePath); return Result.success(null); } catch (Exception e) { e.printStackTrace(); return Result.fail("删除失败" + e.getMessage()); } } }
|
六. 上传流程
1 2 3 4 5 6 7 8 9 10
| 前端: 1.准备el-upload上传组件 2.当我们点击上传按钮的时候,其实是在触发el-upload的action 3.action调用后端接口:http://localhost:8080/fastDfs 4.后端上传成功之后,返回到el-upload的:on-success="handleSuccess" 5.在handleSuccess中,将后端返回的resultObj绑定给模型层的logo 后端: 1.接收请求@RequestPart MultipartFile file 2.调用FastDfsUtils完成文件上传得到返回值path 3.然后将path设置给封装结果类的data传回给前端
|
七. 文件上传表单项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <el-form-item prop="logo" label="店铺Logo"> <!-- :on-preview="handlePreview" - 点击图片名时触发此函数 - 可以在这里做放大功能,这里不做 :on-remove="handleRemove" - 点击删除时触发 :on-success="handleSuccess" - 上传成功之后触发 :file-list="fileList" - 文件列表 action="http://localhost:8080/fastDfs/" - 文件上传接口地址 --> <el-upload class="upload-demo" action="http://localhost:8080/fastDfs/" :on-preview="handlePreview" :on-remove="handleRemove" :on-success="handleSuccess" :file-list="fileList" list-type="picture"> <el-button size="small" type="primary">点击上传</el-button> <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div> </el-upload> </el-form-item>
|
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| handleSuccess(response, file, fileList) { this.shop.logo = response.data; console.log(response.data) },
handleRemove(file, fileList) { var filePath = file.response.data; this.$http.delete("/fastDfs?path=" + filePath) .then(res => { if (res.data.success) { this.$message({ message: '删除成功!', type: 'success' }); } else { this.$message({ message: '删除失败!', type: 'error' }); } })
|