一,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> {
}
