1.简介
FastDFS是一种开源轻量级分布式文件上传系统,它对文件的管理包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决大容量存储负载均衡问题。
因为其搭建成本高、维护成本高不适合小型项目,但是其文件是存储在存储服务器中安全性较高。
FastDFS架构图:
FastDFS分为2部分,Tracker Server端,Storager Server端;
Tracker Server端为FastDFS的管理端,Tracker Server集群一个主机多个备机,主备之间通过心跳检测保证管理端的高可用,用户请求存储文件首先调用Tracker Server,
内部实现负载均衡,返回给客户端Client一个Storager Server服务器地址,客户端获取Storager Server地址值后调用存储,存储结束后返回客户端一个存储路径。
Storager Server端为存储端,由多态存储服务器集群,一台服务器由一主机和一台备机组成,主机存储后冗余备份至备机,实现存储服务的容灾特性。
上传时序图:
上传后返回路径:
group1:组名
M00:虚拟磁盘路径
02/44/:存储服务器在虚拟磁盘看目录下创建2级目录,用于存储文件
xxxxxxxx.sh:文件名,通过特定的条件生成的不可重复的文件名
2.实现步骤
2.1导入相关依赖包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>net.oschina.zcx7878</groupId> <artifactId>fastdfs-client-java</artifactId> <version>1.27.0.0</version> </dependency>
2.2在服务resources目录下创建fdfs_client.conf配置文件,配置上传参数
connect_timeout = 60 network_timeout = 60 charset = UTF-8 http.tracker_http_port = 8080 # 文件访问端口号 tracker_server = 192.168.200.128:22122 # 文件改动端口号
2.3创建application.yml配置文件
spring: servlet: multipart: max-file-size: 10MB max-request-size: 10MB server: port: 9008 eureka: client: service-url: defaultZone: http://127.0.0.1:6868/eureka instance: prefer-ip-address: true feign: hystrix: enabled: true
2.4创建文件上传实体类
/** * 文件上传实体类 */ public class FastDFSFile { //文件名字 private String name; //文件内容 private byte[] content; //文件扩展名 private String ext; //文件MD5摘要值 private String md5; //文件创建作者 private String author; public FastDFSFile(String name, byte[] content, String ext, String height, String width, String author) { super(); this.name = name; this.content = content; this.ext = ext; this.author = author; } public FastDFSFile(String name, byte[] content, String ext) { super(); this.name = name; this.content = content; this.ext = ext; } public String getName() { return name; } public void setName(String name) { this.name = name; } public byte[] getContent() { return content; } public void setContent(byte[] content) { this.content = content; } public String getExt() { return ext; } public void setExt(String ext) { this.ext = ext; } public String getMd5() { return md5; } public void setMd5(String md5) { this.md5 = md5; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } }
2.4创建文件上传工具类
/** * fastDFS文件上传工具类 */ public class FastDFSClient { private static org.slf4j.Logger logger = LoggerFactory.getLogger(FastDFSClient.class); /*** * 初始化加载FastDFS的TrackerServer配置 */ static { try { String filePath = new ClassPathResource("fdfs_client.conf").getFile().getAbsolutePath();; ClientGlobal.init(filePath); } catch (Exception e) { logger.error("FastDFS Client Init Fail!",e); } } /*** * 文件上传 * @param file * @return */ public static String[] upload(FastDFSFile file) { //获取文件的作者 NameValuePair[] meta_list = new NameValuePair[1]; meta_list[0] = new NameValuePair("author", file.getAuthor()); //接收返回数据 String[] uploadResults = null; StorageClient storageClient=null; try { //创建StorageClient客户端对象 storageClient = getTrackerClient(); /*** * 文件上传 * 1)文件字节数组 * 2)文件扩展名 * 3)文件作者 */ uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), meta_list); } catch (Exception e) { logger.error("Exception when uploadind the file:" + file.getName(), e); } if (uploadResults == null && storageClient!=null) { logger.error("upload file fail, error code:" + storageClient.getErrorCode()); } //获取组名 String groupName = uploadResults[0]; //获取文件存储路径 String remoteFileName = uploadResults[1]; return uploadResults; } /*** * 获取文件信息 * @param groupName:组名 * @param remoteFileName:文件存储完整名 * @return */ public static FileInfo getFile(String groupName, String remoteFileName) { try { StorageClient storageClient = getTrackerClient(); return storageClient.get_file_info(groupName, remoteFileName); } catch (Exception e) { logger.error("Exception: Get File from Fast DFS failed", e); } return null; } /*** * 文件下载 * @param groupName * @param remoteFileName * @return */ public static InputStream downFile(String groupName, String remoteFileName) { try { //创建StorageClient StorageClient storageClient = getTrackerClient(); //下载文件 byte[] fileByte = storageClient.download_file(groupName, remoteFileName); InputStream ins = new ByteArrayInputStream(fileByte); return ins; } catch (Exception e) { logger.error("Exception: Get File from Fast DFS failed", e); } return null; } /*** * 文件删除 * @param groupName * @param remoteFileName * @throws Exception */ public static void deleteFile(String groupName, String remoteFileName) throws Exception { //创建StorageClient StorageClient storageClient = getTrackerClient(); //删除文件 int i = storageClient.delete_file(groupName, remoteFileName); } /*** * 获取Storage组 * @param groupName * @return * @throws IOException */ public static StorageServer[] getStoreStorages(String groupName) throws IOException { //创建TrackerClient TrackerClient trackerClient = new TrackerClient(); //获取TrackerServer TrackerServer trackerServer = trackerClient.getConnection(); //获取Storage组 return trackerClient.getStoreStorages(trackerServer, groupName); } /*** * 获取Storage信息,IP和端口 * @param groupName * @param remoteFileName * @return * @throws IOException */ public static ServerInfo[] getFetchStorages(String groupName, String remoteFileName) throws IOException { TrackerClient trackerClient = new TrackerClient(); TrackerServer trackerServer = trackerClient.getConnection(); return trackerClient.getFetchStorages(trackerServer, groupName, remoteFileName); } /*** * 获取Tracker服务地址 * @return * @throws IOException */ public static String getTrackerUrl() throws IOException { return "http://"+getTrackerServer().getInetSocketAddress().getHostString()+":"+ ClientGlobal.getG_tracker_http_port()+"/"; } /*** * 获取Storage客户端 * @return * @throws IOException */ private static StorageClient getTrackerClient() throws IOException { TrackerServer trackerServer = getTrackerServer(); StorageClient storageClient = new StorageClient(trackerServer, null); return storageClient; } /*** * 获取Tracker * @return * @throws IOException */ private static TrackerServer getTrackerServer() throws IOException { TrackerClient trackerClient = new TrackerClient(); TrackerServer trackerServer = trackerClient.getConnection(); return trackerServer; } }
2.5编写文件上传controller类
/** * 文件操作controller * @author ZJ */ @RestController @RequestMapping("/file") public class FileController { /** * <input type="file" name="file"/> * @param file * @return */ @PostMapping public Result fileUpload(MultipartFile file) { try { //1. 获取完整的文件名 String fileName = file.getOriginalFilename(); //2. 获取文件扩展名 if (StringUtils.isEmpty(fileName)) { throw new RuntimeException("文件不存在"); } String ext = fileName.substring(fileName.lastIndexOf(".")); //3. 获取文件内容 byte[] contents = file.getBytes(); //4. 创建上传文件实体类 FastDFSFile fastDFSFile = new FastDFSFile(fileName, contents, ext); //5. 调用上传文件工具方法, 上传, 并返回存储后的文件路径和文件名 String[] path = FastDFSClient.upload(fastDFSFile); //6. 返回上传后的路径和文件名 String url = FastDFSClient.getTrackerUrl() + path[0] + "/" + path[1]; return new Result(true, StatusCode.OK, "上传成功", path); } catch (IOException e) { e.printStackTrace(); } return new Result(false, StatusCode.ERROR, "上传失败"); } }