一,FastDFS简介
FastDFS是用c语言编写的一款开源的分布式文件系统,它是由淘宝资深架构师余庆编写并开源。FastDFS专为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
优势·:
适合通用分部式,fastDFS非常适合存储图片等那些小文件,fastDFS不对文件进行分块,所以它就没有分块合并的开销,fastDFS网络通信采用socket,通信速度很快。
二,服务器分为tracker和storage
三,上传文件原理
storage将文件id返回客户端
id中包含的信息
代码实现:
1.工具类:
1 import org.csource.common.NameValuePair; 2 import org.csource.fastdfs.*; 3 import org.slf4j.LoggerFactory; 4 import org.springframework.core.io.ClassPathResource; 5 6 import java.io.ByteArrayInputStream; 7 import java.io.IOException; 8 import java.io.InputStream; 9 10 public class FastDFSClient { 11 private static org.slf4j.Logger logger = LoggerFactory.getLogger(FastDFSClient.class); 12 13 /*** 14 * 初始化加载FastDFS的TrackerServer配置 15 */ 16 static { 17 try { 18 String filePath = new ClassPathResource("fdfs_client.conf").getFile().getAbsolutePath();; 19 ClientGlobal.init(filePath); 20 } catch (Exception e) { 21 logger.error("FastDFS Client Init Fail!",e); 22 } 23 } 24 25 /*** 26 * 文件上传 27 * @param file 28 * @return 29 */ 30 public static String[] upload(FastDFSFile file) { 31 //获取文件的作者 32 NameValuePair[] meta_list = new NameValuePair[1]; 33 meta_list[0] = new NameValuePair("author", file.getAuthor()); 34 35 //接收返回数据 36 String[] uploadResults = null; 37 StorageClient storageClient=null; 38 try { 39 //创建StorageClient客户端对象 40 storageClient = getTrackerClient(); 41 42 /*** 43 * 文件上传 44 * 1)文件字节数组 45 * 2)文件扩展名 46 * 3)文件作者 47 */ 48 uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), meta_list); 49 } catch (Exception e) { 50 logger.error("Exception when uploadind the file:" + file.getName(), e); 51 } 52 53 if (uploadResults == null && storageClient!=null) { 54 logger.error("upload file fail, error code:" + storageClient.getErrorCode()); 55 } 56 //获取组名 57 String groupName = uploadResults[0]; 58 //获取文件存储路径 59 String remoteFileName = uploadResults[1]; 60 return uploadResults; 61 } 62 63 /*** 64 * 获取文件信息 65 * @param groupName:组名 66 * @param remoteFileName:文件存储完整名 67 * @return 68 */ 69 public static FileInfo getFile(String groupName, String remoteFileName) { 70 try { 71 StorageClient storageClient = getTrackerClient(); 72 return storageClient.get_file_info(groupName, remoteFileName); 73 } catch (Exception e) { 74 logger.error("Exception: Get File from Fast DFS failed", e); 75 } 76 return null; 77 } 78 79 /*** 80 * 文件下载 81 * @param groupName 82 * @param remoteFileName 83 * @return 84 */ 85 public static InputStream downFile(String groupName, String remoteFileName) { 86 try { 87 //创建StorageClient 88 StorageClient storageClient = getTrackerClient(); 89 90 //下载文件 91 byte[] fileByte = storageClient.download_file(groupName, remoteFileName); 92 InputStream ins = new ByteArrayInputStream(fileByte); 93 return ins; 94 } catch (Exception e) { 95 logger.error("Exception: Get File from Fast DFS failed", e); 96 } 97 return null; 98 } 99 100 101 /*** 102 * 文件删除 103 * @param groupName 104 * @param remoteFileName 105 * @throws Exception 106 */ 107 public static void deleteFile(String groupName, String remoteFileName) 108 throws Exception { 109 //创建StorageClient 110 StorageClient storageClient = getTrackerClient(); 111 112 //删除文件 113 int i = storageClient.delete_file(groupName, remoteFileName); 114 } 115 116 117 /*** 118 * 获取Storage组 119 * @param groupName 120 * @return 121 * @throws IOException 122 */ 123 public static StorageServer[] getStoreStorages(String groupName) 124 throws IOException { 125 //创建TrackerClient 126 TrackerClient trackerClient = new TrackerClient(); 127 //获取TrackerServer 128 TrackerServer trackerServer = trackerClient.getConnection(); 129 //获取Storage组 130 return trackerClient.getStoreStorages(trackerServer, groupName); 131 } 132 133 /*** 134 * 获取Storage信息,IP和端口 135 * @param groupName 136 * @param remoteFileName 137 * @return 138 * @throws IOException 139 */ 140 public static ServerInfo[] getFetchStorages(String groupName, 141 String remoteFileName) throws IOException { 142 TrackerClient trackerClient = new TrackerClient(); 143 TrackerServer trackerServer = trackerClient.getConnection(); 144 return trackerClient.getFetchStorages(trackerServer, groupName, remoteFileName); 145 } 146 147 /*** 148 * 获取Tracker服务地址 149 * @return 150 * @throws IOException 151 */ 152 public static String getTrackerUrl() throws IOException { 153 return "http://"+getTrackerServer().getInetSocketAddress().getHostString()+":"+ ClientGlobal.getG_tracker_http_port()+"/"; 154 } 155 156 157 /*** 158 * 获取Storage客户端 159 * @return 160 * @throws IOException 161 */ 162 private static StorageClient getTrackerClient() throws IOException { 163 TrackerServer trackerServer = getTrackerServer(); 164 StorageClient storageClient = new StorageClient(trackerServer, null); 165 return storageClient; 166 } 167 168 169 /*** 170 * 获取Tracker 171 * @return 172 * @throws IOException 173 */ 174 private static TrackerServer getTrackerServer() throws IOException { 175 TrackerClient trackerClient = new TrackerClient(); 176 TrackerServer trackerServer = trackerClient.getConnection(); 177 return trackerServer; 178 } 179 }
2.启动类
1 import org.springframework.boot.SpringApplication; 2 import org.springframework.boot.autoconfigure.SpringBootApplication; 3 import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 4 5 @SpringBootApplication 6 @EnableEurekaClient 7 public class FileApplication { 8 public static void main(String[] args) { 9 SpringApplication.run( FileApplication.class ); 10 } 11 }
3.FastDFSFile标准类
1 public class FastDFSFile { 2 3 //文件名字 4 private String name; 5 //文件内容 6 private byte[] content; 7 //文件扩展名 8 private String ext; 9 //文件MD5摘要值 10 private String md5; 11 //文件创建作者 12 private String author; 13 14 public FastDFSFile(String name, byte[] content, String ext, String height, 15 String width, String author) { 16 super(); 17 this.name = name; 18 this.content = content; 19 this.ext = ext; 20 this.author = author; 21 } 22 23 public FastDFSFile(String name, byte[] content, String ext) { 24 super(); 25 this.name = name; 26 this.content = content; 27 this.ext = ext; 28 } 29 30 public String getName() { 31 return name; 32 } 33 34 public void setName(String name) { 35 this.name = name; 36 } 37 38 public byte[] getContent() { 39 return content; 40 } 41 42 public void setContent(byte[] content) { 43 this.content = content; 44 } 45 46 public String getExt() { 47 return ext; 48 } 49 50 public void setExt(String ext) { 51 this.ext = ext; 52 } 53 54 public String getMd5() { 55 return md5; 56 } 57 58 public void setMd5(String md5) { 59 this.md5 = md5; 60 } 61 62 public String getAuthor() { 63 return author; 64 } 65 66 public void setAuthor(String author) { 67 this.author = author; 68 } 69 }
4.上传的类
1 import org.springframework.web.bind.annotation.CrossOrigin; 2 import org.springframework.web.bind.annotation.PostMapping; 3 import org.springframework.web.bind.annotation.RequestParam; 4 import org.springframework.web.bind.annotation.RestController; 5 import org.springframework.web.multipart.MultipartFile; 6 7 import java.io.IOException; 8 9 @RestController 10 @CrossOrigin 11 public class FileController { 12 13 14 @PostMapping("/upload") 15 public String upload( @RequestParam("file") MultipartFile file){ 16 try { 17 //1.获取文件名 18 String fileName = file.getOriginalFilename(); 19 //2.获取文件内容 20 byte[] bytes = file.getBytes(); 21 //3.获取文件扩展名 . 22 String ext = fileName.substring( fileName.lastIndexOf( "." ) ); 23 //4.封装文件实体 24 FastDFSFile fastDFSFile=new FastDFSFile( fileName,bytes,ext ); 25 //5.文件上传 26 String[] result = FastDFSClient.upload( fastDFSFile ); 27 //6.返回结果 28 String path = FastDFSClient.getTrackerUrl()+result[0]+"/"+result[1]; 29 return path; 30 31 } catch (IOException e) { 32 e.printStackTrace(); 33 return ""; 34 } 35 } 36 37 }
yml文件
1 server: 2 port: 9008 3 spring: 4 application: 5 name: file 6 servlet: 7 multipart: 8 max-file-size: 10MB 9 max-request-size: 10MB 10 main: 11 allow-bean-definition-overriding: true #当遇到同样名字的时候,是否允许覆盖注册 12 eureka: 13 client: 14 service-url: 15 defaultZone: http://127.0.0.1:6868/eureka 16 instance: 17 prefer-ip-address: true
配置文件
connect_timeout = 60 network_timeout = 60 charset = UTF-8 http.tracker_http_port = 8080 tracker_server = 192.168.200.128:22122
四,文件下载
原理
有tracker根据id中的信息来决定从哪个storage中查询
五,项目级别运用
(一)图片文件上传(对应1-5)
1.文件校验(2)
//文件校验
if (file == null){
ExceptionCast.cast(CommonCode.INVALIDATE_PARAMS);
}
2.上传文件(3)
1 2 //上传文件 3 String fileId = this.uploadFileToFastdfs(file); 4 if (StringUtils.isEmpty(fileId)){ 5 ExceptionCast.cast(FileSystemCode.FS_UPLOADFILE_FILEISNULL); 6 } 7 8 private String uploadFileToFastdfs(MultipartFile file) { 9 10 11 try { 12 13 //初始化fastdfs 14 this.initFastdfs(); 15 16 //编写上传功能 17 TrackerClient trackerClient = new TrackerClient(); 18 TrackerServer trackerServer = null; 19 trackerServer = trackerClient.getConnection(); 20 StorageServer storageServer = trackerClient.getStoreStorage(trackerServer); 21 StorageClient1 storageClient1 = new StorageClient1(trackerServer,storageServer); 22 23 String originalFilename = file.getOriginalFilename(); 24 String extName = originalFilename.substring(originalFilename.lastIndexOf(".")+1); 25 26 String fileId = storageClient1.upload_file1(file.getBytes(), extName, null); 27 return fileId; 28 29 } catch (Exception e) { 30 e.printStackTrace(); 31 } 32 33 return null; 34 35 36 } 37 38 39 private void initFastdfs() { 40 try { 41 ClientGlobal.initByTrackers(tracker_servers); 42 ClientGlobal.setG_network_timeout(network_timeout_in_seconds); 43 ClientGlobal.setG_connect_timeout(connect_timeout_in_seconds); 44 ClientGlobal.setG_charset(charset); 45 } catch (Exception e) { 46 e.printStackTrace(); 47 } 48 }
初始化fastDfs1
private void initFastdfs() { try { ClientGlobal.initByTrackers(tracker_servers); ClientGlobal.setG_network_timeout(network_timeout_in_seconds); ClientGlobal.setG_connect_timeout(connect_timeout_in_seconds); ClientGlobal.setG_charset(charset); } catch (Exception e) { e.printStackTrace(); } }
,编写上传功能(4返回文件地址)
String fileId = storageClient1.upload_file1(file.getBytes(), extName, null);
return fileId;
1 //编写上传功能 2 TrackerClient trackerClient = new TrackerClient(); 3 TrackerServer trackerServer = null; 4 trackerServer = trackerClient.getConnection(); 5 StorageServer storageServer = trackerClient.getStoreStorage(trackerServer); 6 StorageClient1 storageClient1 = new StorageClient1(trackerServer,storageServer); 7 8 String originalFilename = file.getOriginalFilename(); 9 String extName = originalFilename.substring(originalFilename.lastIndexOf(".")+1); 10 11 String fileId = storageClient1.upload_file1(file.getBytes(), extName, null); 12 return fileId; 13 14 } catch (Exception e) { 15 e.printStackTrace(); 16 } 17 18 return null;
3.存到mongodb(5,文件信息存到mongo库)
1 //将文件的id存入mongodb 2 FileSystem fileSystem = new FileSystem(); 3 4 //设置值 5 //文件id 6 fileSystem.setFileId(fileId); 7 //文件在文件系统中的路径 8 fileSystem.setFilePath(fileId); 9 //业务标识 10 fileSystem.setBusinesskey(businesskey); 11 //标签 12 fileSystem.setFiletag(filetag); 13 //元数据 14 if(StringUtils.isNotEmpty(metadata)){ 15 try { 16 Map map = JSON.parseObject(metadata, Map.class); 17 fileSystem.setMetadata(map); 18 } catch (Exception e) { 19 e.printStackTrace(); 20 } 21 } 22 //名称 23 fileSystem.setFileName(file.getOriginalFilename()); 24 //大小 25 fileSystem.setFileSize(file.getSize()); 26 //文件类型 27 fileSystem.setFileType(file.getContentType()); 28 29 fileSystemRepository.save(fileSystem); 30 31 //返回结果 32 return new UploadFileResult(CommonCode.SUCCESS,fileSystem); 33 }
(二)图片信息和课程信息绑定,并存到mysql(6-8)
课程管理服务负责将(一)中上传的文件的图片地址,与对应课程id进行绑定并保存在mysql数据库,方便其他子系统使用。
添加方法
查询方法
删除方法
1 @Override 2 @PostMapping("/coursepic/add") 3 public ResponseResult addCoursePic(String courseId, String pic) { 4 return courseService.addCoursePic(courseId,pic); 5 } 6 7 8 @Override 9 @PreAuthorize("hasAuthority('course_pic_list')") 10 @GetMapping("/coursepic/list/{courseId}") 11 public CoursePic findCoursePic(@PathVariable("courseId") String courseId) { 12 return courseService.findCoursePic(courseId); 13 } 14 @Override 15 @DeleteMapping("/coursepic/delete") 16 public ResponseResult delCoursePic(String courseId) { 17 return courseService.delCoursePic(courseId); 18 }
servcie
/** * 保存课程图片信息 * @param courseId * @param pic * @return */ public ResponseResult addCoursePic(String courseId, String pic) { Optional<CoursePic> coursePicOptional = coursePicRepository.findById(courseId); CoursePic coursePic = null; if (coursePicOptional.isPresent()){ coursePic = coursePicOptional.get(); } if (coursePic == null){ coursePic = new CoursePic(); } coursePic.setPic(pic); coursePic.setCourseid(courseId); coursePicRepository.save(coursePic); return new ResponseResult(CommonCode.SUCCESS); } /** * 查询课程图片 * @param courseId * @return */ public CoursePic findCoursePic(String courseId) { Optional<CoursePic> coursePicOptional = coursePicRepository.findById(courseId); if (coursePicOptional.isPresent()){ return coursePicOptional.get(); } return null; } /** * 删除课程图片 * @param courseId 课程id * @return ResponseResult(规范返回类型) */ public ResponseResult delCoursePic(String courseId) { coursePicRepository.deleteById(courseId); return new ResponseResult(CommonCode.SUCCESS); }
dao
import com.xuecheng.filesystem.framework.domain.course.response.CoursePic;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CoursePicRepository extends JpaRepository<CoursePic,String> {
}