二. fastDFS:springboot 整合fastDFS
阿新 • • 發佈:2019-01-06
一.引入官方客戶端依賴
<!--FastDFS-->
<dependency>
<groupId>net.oschina.zcx7878</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27.0.0</version>
</dependency>
<!--pool--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.4.2</version> </dependency>
-- 以上只列舉相關的依賴,其它依賴自行新增
二 . 配置檔案
#######################FastDFS####################### #檔案伺服器地址 file_server_addr=192.168.17.128:80 # 最大連線數 併發量較大的話可加大該連線數 max_storage_connection=10 #超時配置 fastdfs.connect_timeout_in_seconds=100000 fastdfs.network_timeout_in_seconds=300000 fastdfs.charset=UTF-8 #token 防盜鏈功能 fastdfs.http_anti_steal_token=true #金鑰,預設為FASTDFS1234567890 fastdfs.http_secret_key=FASTDFS1234567890 # TrackerServer port fastdfs.http_tracker_http_port=6666 ## Tracker Server, if more than one, separate with "," fastdfs.tracker_servers=192.168.17.128:22122 #######################FastDFS#######################
-- FastDFS的配置檔案只支援properties型別的,可以整合放入application.properties裡面,但是不能放入yml型別的裡面
三.服務端程式碼編寫
0.啟動類配置
SpringbootFastdfsApplication.class
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) @EnableAutoConfiguration @MapperScan(value = "com.vesus.springbootfastdfs.mapper") @Configuration @PropertySource(value = "classpath:fastdfs.properties",encoding = "utf-8") public class SpringbootFastdfsApplication extends SpringBootServletInitializer { public static void main(String[] args) { SpringApplication.run(SpringbootFastdfsApplication.class, args); } /** * war 包部署 * @param application * @return */ @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(SpringbootFastdfsApplication.class); } }
--
1.編寫排程tracker池
TrackerServerPool.class
public class TrackerServerPool {
private static Logger logger= LoggerFactory.getLogger(TrackerServerPool.class);
/***
* 配置檔案路徑
*/
private static final String FASTDFS_CONFIG_PATH = "fastdfs.properties";
/***
* 最大連線數
*/
@Value("${max_storage_connection}")
private static int maxStorageConnection ;
/***
* TrackerServer 物件池
*/
private static GenericObjectPool<TrackerServer> pool ;
/***
* 無參建構函式
*/
private TrackerServerPool(){}
/***
* 獲取TrackerServer連線池
* @return
* @throws Exception
*/
public static synchronized GenericObjectPool<TrackerServer> getObjectPool() throws Exception{
if (pool ==null){
//載入配置檔案
ClientGlobal.initByProperties(FASTDFS_CONFIG_PATH);
//pool配置,設定最大值和最小值
GenericObjectPoolConfig config = new GenericObjectPoolConfig();
config.setMinIdle(2);
if (maxStorageConnection>0){
config.setMaxIdle(maxStorageConnection);
}
pool = new GenericObjectPool<>(new TrackerServerFactory(),config) ;
}
return pool ;
}
/***
* 獲取TrackerServer
* @return
* @throws Exception
*/
public static TrackerServer borrowObject()throws Exception{
TrackerServer trackerServer = null;
trackerServer = getObjectPool().borrowObject() ;
return trackerServer ;
}
/***
* 回收 TrackerServer
* @param server
* @throws Exception
*/
public static void recycleObject(TrackerServer server) throws Exception{
getObjectPool().returnObject(server);
}
}
2.fastdfs排程Tracker工廠
TrackerServerFactory.class
public class TrackerServerFactory extends BasePooledObjectFactory<TrackerServer> {
public TrackerServer create() throws Exception {
//例項化TrackerClient
TrackerClient client = new TrackerClient();
//獲取TrackerServer
TrackerServer trackerServer = client.getConnection();
return trackerServer;
}
public PooledObject<TrackerServer> wrap(TrackerServer server) {
return new DefaultPooledObject<TrackerServer>(server);
}
}
3.編寫fastdfs操作客戶端
FastDFSClient.class
@Component
public class FastDFSClient {
/**
* 路徑分隔符
*/
public static final String SEPARATOR = "/";
/**
* Point
*/
public static final String POINT = ".";
/**
* 檔案型別
*/
public static final Map<String, String> EXT_MAPS = new HashMap<>();
/**
* 日誌
*/
private static Logger logger = LoggerFactory.getLogger(FastDFSClient.class);
/**
* 檔名稱
*/
private static final String FILENAME = "filename";
/**
* 檔案最大的大小
*/
private int maxFileSize = 500 * 1024 * 1024;
public FastDFSClient(){
initExt();
}
private void initExt(){
//圖片
EXT_MAPS.put("png", "image/png");
EXT_MAPS.put("gif", "image/gif");
EXT_MAPS.put("bmp", "image/bmp");
EXT_MAPS.put("ico", "image/x-ico");
EXT_MAPS.put("jpeg", "image/jpeg");
EXT_MAPS.put("jpg", "image/jpeg");
//壓縮檔案
EXT_MAPS.put("zip", "application/zip");
EXT_MAPS.put("rar", "application/x-rar");
//word,excel,ppt
EXT_MAPS.put("pdf", "application/pdf");
EXT_MAPS.put("ppt", "application/vnd.ms-powerpoint");
EXT_MAPS.put("xls", "application/vnd.ms-excel");
EXT_MAPS.put("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
EXT_MAPS.put("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation");
EXT_MAPS.put("doc", "application/msword");
EXT_MAPS.put("doc", "application/wps-office.doc");
EXT_MAPS.put("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
EXT_MAPS.put("txt", "text/plain");
//視訊、音訊
EXT_MAPS.put("mp4", "video/mp4");
EXT_MAPS.put("flv", "video/x-flv");
}
/***
* 獲取檔案最大值
* @return
*/
public int getMaxFileSize(){
return maxFileSize ;
}
/***
* 設定檔案的最大值
* @param value
*/
public void setMaxFileSize(int value){
this.maxFileSize = value ;
}
/***
* 獲取FastDFS檔案的名稱,如:M00/00/00/rBBNW1sJCvCAHgH7AAVGh5jPYok707.jpg
* @param fileid fileId 包含組名和檔名,如:group1/M00/00/00/rBBNW1sJCvCAHgH7AAVGh5jPYok707.jpg
* @return
*/
public static String getFilename(String fileid){
String[] results = new String[2];
StorageClient1.split_file_id(fileid,results);
return results[1] ;
}
/***
* 獲取檔案描述資訊
* @param filePath
* @return
* @throws Exception
*/
public Map<String,Object> getFileDescriptions(String filePath)throws Exception{
//獲取trackerserver
TrackerServer trackerServer = TrackerServerPool.borrowObject();
StorageClient1 storageClient1 = new StorageClient1(trackerServer,null) ;
NameValuePair[] pairs = null ;
//獲取檔案元資料
pairs = storageClient1.get_metadata1(filePath) ;
//回收trackerServer
TrackerServerPool.recycleObject(trackerServer);
Map<String, Object> infoMap = null;
if (pairs != null && pairs.length > 0) {
infoMap = new HashMap<>(pairs.length);
for (NameValuePair pair : pairs) {
infoMap.put(pair.getName(), pair.getValue());
}
}
return infoMap;
}
/***
* 獲取原始檔的檔名稱
* @param filePath
* @return
* @throws Exception
*/
public String getOriginalFileName(String filePath) throws Exception{
//獲取檔案的描述資訊
Map<String,Object> descriptions = getFileDescriptions(filePath);
//獲取檔名稱
if (descriptions.get(FILENAME)!=null){
return (String) descriptions.get(FILENAME);
}
return null ;
}
/***
* 獲取檔名稱的字尾
* @param fileName
* @return
*/
public static String getFIleNameSuffix(String fileName){
String suffix = null ;
if (fileName!=null&&!"".equals(fileName)){
if (fileName.contains(SEPARATOR)){
fileName = fileName.substring(fileName.lastIndexOf(SEPARATOR) + 1);
}
if (fileName.contains(POINT)){
suffix = fileName.substring(fileName.lastIndexOf(POINT) + 1);
} else {
if (logger.isErrorEnabled()) {
logger.error("檔案字尾獲取失敗!");
}
}
}
return suffix ;
}
/***
* 獲取檔案資訊
* @param filePath
* @return
* @throws Exception
*/
public Map<String,Object> getFileInfo(String filePath) throws Exception{
//獲取trackerserver
TrackerServer trackerServer = TrackerServerPool.borrowObject();
StorageClient1 storageClient1 = new StorageClient1(trackerServer,null) ;
FileInfo fileInfo = null ;
//獲取檔案資料
fileInfo = storageClient1.get_file_info1(filePath);
// 返還物件
TrackerServerPool.recycleObject(trackerServer);
Map<String, Object> infoMap = new HashMap<>(4);
//源IP
infoMap.put("SourceIpAddr", fileInfo.getSourceIpAddr());
//檔案大小
infoMap.put("FileSize", fileInfo.getFileSize());
//建立時間
infoMap.put("CreateTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(fileInfo.getCreateTimestamp()));
//簽名
infoMap.put("CRC32", fileInfo.getCrc32());
return infoMap;
}
/***
* 轉換路徑中的 '\' 為 '/' ,並把檔案字尾轉為小寫
* @param path
* @return
*/
public static String toLocal(String path) {
if (path!=null&&!"".equals(path)) {
path = path.replaceAll("\\\\", SEPARATOR);
if (path.contains(POINT)) {
String pre = path.substring(0, path.lastIndexOf(POINT) + 1);
String suffix = path.substring(path.lastIndexOf(POINT) + 1).toLowerCase();
path = pre + suffix;
}
}
return path;
}
/**
* MultipartFile 上傳檔案
*
* @param file
* @return 返回檔案路徑
*/
public String uploadFileWithMultipart(MultipartFile file) throws Exception {
return upload(file, null);
}
/**
* MultipartFile 上傳檔案
*
* @param file
* @param descriptions
* @return 返回檔案路徑
*/
public String uploadFileWithMultipart(MultipartFile file, Map<String, String> descriptions) throws Exception {
return upload(file, descriptions);
}
/**
* 根據指定的路徑上傳檔案
*
* @param filepath
* @return 返回檔案路徑
*/
public String uploadFileWithFilepath(String filepath) throws Exception {
return upload(filepath, null);
}
/**
* 根據指定的路徑上傳檔案
*
* @param filepath
* @param descriptions
* @return 返回檔案路徑
*/
public String uploadFileWithFilepath(String filepath, Map<String, String> descriptions) throws Exception {
return upload(filepath, descriptions);
}
/**
* 上傳base64檔案
*
* @param base64
* @return 返回檔案路徑
*/
public String uploadFileWithBase64(String base64) throws Exception {
return upload(base64, null, null);
}
/**
* 上傳base64檔案
*
* @param base64
* @param filename
* @return 返回檔案路徑
*/
public String uploadFileWithBase64(String base64, String filename) throws Exception {
return upload(base64, filename, null);
}
/**
* 上傳base64檔案
*
* @param base64
* @param filename
* @param descriptions
* @return 返回檔案路徑
*/
public String uploadFileWithBase64(String base64, String filename, Map<String, String> descriptions) throws Exception {
return upload(base64, filename, descriptions);
}
/***
* 上傳通用方法
* @param is 檔案流
* @param fileName 檔名稱
* @param descriptions
* @return 組名+檔案路徑
* @throws Exception
*/
public String upload(InputStream is, String fileName, Map<String, String> descriptions) throws Exception {
if(is == null){
throw new Exception("檔案為空!");
}
if(is.available() > maxFileSize){
throw new Exception("檔案太大!");
}
fileName = toLocal(fileName);
// 返回路徑
String path = null;
// 檔案描述
NameValuePair[] nvps = null;
List<NameValuePair> nvpsList = new ArrayList<>();
// 檔名字尾
String suffix = getFIleNameSuffix(fileName);
// 檔名
if (fileName!=null&&!"".equals(fileName)) {
nvpsList.add(new NameValuePair(FILENAME, fileName));
}
// 描述資訊
if (descriptions != null && descriptions.size() > 0) {
descriptions.forEach((key, value) -> {
nvpsList.add(new NameValuePair(key, value));
});
}
if (nvpsList.size() > 0) {
nvps = new NameValuePair[nvpsList.size()];
nvpsList.toArray(nvps);
}
TrackerServer trackerServer = TrackerServerPool.borrowObject();
StorageClient1 storageClient = new StorageClient1(trackerServer, null);
try {
// 讀取流
byte[] fileBuff = new byte[is.available()];
is.read(fileBuff, 0, fileBuff.length);
// 上傳
path = storageClient.upload_file1(fileBuff, suffix, nvps);
if(path==null||"".equals(path)) {
throw new Exception("上傳失敗!");
}
if (logger.isDebugEnabled()) {
logger.debug("上傳成功!", path);
}
} catch (IOException e) {
e.printStackTrace();
} catch (MyException e) {
e.printStackTrace();
} finally {
// 關閉流
if(is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 回收物件
TrackerServerPool.recycleObject(trackerServer);
return path;
}
/****
* 根據指定的路徑上傳
* @param filePath
* @param descriptions
* @return
* @throws Exception
*/
public String upload(String filePath ,Map<String,String> descriptions) throws Exception{
if (filePath==null||"".equals(filePath)){
throw new Exception("檔案路徑為空!") ;
}
File file = new File(filePath);
String path = null ;
//獲取檔案流
InputStream is = new FileInputStream(file);
// 獲取檔名
filePath = toLocal(filePath);
String filename = filePath.substring(filePath.lastIndexOf("/") + 1);
//上傳
path = upload(is, filename, descriptions);
return path ;
}
/***
* 使用 MultipartFile 上傳
* @param file
* @param descriptions
* @return 檔案路徑
* @throws Exception
*/
public String upload(MultipartFile file, Map<String, String> descriptions) throws Exception {
if(file == null || file.isEmpty()){
throw new Exception("檔案為空!");
}
String path = null;
try {
path = upload(file.getInputStream(), file.getOriginalFilename(), descriptions);
} catch (IOException e) {
e.printStackTrace();
}
return path;
}
/***
* 上傳base64檔案
* @param base64
* @param filename
* @param descriptions
* @return
* @throws Exception
*/
public String upload(String base64, String filename, Map<String, String> descriptions) throws Exception {
if(base64==null||"".equals(base64)){
throw new Exception("檔案為空!");
}
return upload(new ByteArrayInputStream(Base64.decodeBase64(base64)), filename, descriptions);
}
/***
* 獲取訪問伺服器的token
* @param filePath group1/M00/00/00/rBBNW1sJCvCAHgH7AAVGh5jPYok707.jpg
* @param secret 金鑰
* @return 返回token
*/
public static String getToken(String filePath,String secret) throws Exception{
//獲取當前時間毫秒數
int ts = (int) Instant.now().getEpochSecond();
String token ="" ;
try {
token = ProtoCommon.getToken(getFilename(filePath),ts,secret) ;
}catch (Exception e){
e.printStackTrace();
}
return token ;
}
/**
* 以附件形式下載檔案
*
* @param filepath
* @param response
*/
public void downloadFile(String filepath, HttpServletResponse response) throws Exception {
download(filepath, null, null, response);
}
/**
* 下載檔案 輸出檔案
*
* @param filepath
* @param os 輸出流
*/
public void downloadFile(String filepath, OutputStream os) throws Exception {
download(filepath, null, os, null);
}
/**
* 以附件形式下載檔案 可以指定檔名稱.
*
* @param filepath
* @param filename
* @param response
*/
public void downloadFile(String filepath, String filename, HttpServletResponse response) throws Exception {
download(filepath, filename, null, response);
}
/**
* 下載檔案
*
* @param filepath
* @param fileName
* @param os 輸出流
* @param response
*/
public void download(String filepath, String fileName, OutputStream os, HttpServletResponse response) throws Exception {
if(fileName!=null&&!"".equals(fileName)){
throw new Exception("檔案路徑為空!");
}
filepath = toLocal(filepath);
// 檔名
if (fileName==null||"".equals(fileName)) {
fileName = getOriginalFileName(filepath);
}
//獲取檔案型別
String contentType = EXT_MAPS.get(getFIleNameSuffix(fileName));
//獲取trackerServer
TrackerServer trackerServer = TrackerServerPool.borrowObject();
StorageClient1 storageClient = new StorageClient1(trackerServer, null);
InputStream is = null;
try {
// 下載
byte[] fileByte = storageClient.download_file1(filepath);
if(fileByte == null){
throw new Exception("下載失敗!");
}
if (response != null) {
os = response.getOutputStream();
// 設定響應頭
if (contentType!=null||!"".equals(contentType)) {
// 檔案編碼
String encoderName = URLEncoder.encode(fileName, "UTF-8").replace("+", "%20").replace("%2B", "+");
response.setHeader("Content-Disposition", "attachment;filename=\"" + encoderName + "\"");
response.setContentType(contentType + ";charset=UTF-8");
response.setHeader("Accept-Ranges", "bytes");
}
}
is = new ByteArrayInputStream(fileByte);
byte[] buffer = new byte[1024 * 5];
int len = 0;
while ((len = is.read(buffer)) > 0) {
os.write(buffer, 0, len);
}
os.flush();
} catch (IOException e) {
e.printStackTrace();
} catch (MyException e) {
e.printStackTrace();
throw new Exception("下載失敗!");
} finally {
// 關閉流
try {
if(is != null){
is.close();
}
if(os != null){
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 回收物件
TrackerServerPool.recycleObject(trackerServer);
}
/**
* 刪除檔案
*
* @param filepath
* @return 刪除成功返回 0, 失敗返回其它
*/
public int deleteFile(String filepath) throws Exception {
if(filepath==null||"".equals(filepath)){
throw new Exception("檔案路徑為空!");
}
TrackerServer trackerServer = TrackerServerPool.borrowObject();
StorageClient1 storageClient = new StorageClient1(trackerServer, null);
int success = 0;
try {
success = storageClient.delete_file1(filepath);
if(success != 0){
throw new Exception("刪除失敗!");
}
} catch (IOException e) {
e.printStackTrace();
} catch (MyException e) {
e.printStackTrace();
throw new Exception("刪除失敗!");
}
// 回收物件
TrackerServerPool.recycleObject(trackerServer);
return success;
}
}
4. 測試controller
FileController.class
@Controller
public class FileController {
@Autowired
FastDFSClient fastDFSClient ;
@Autowired
private IItemService itemService;
@Value("${fastdfs.http_secret_key}")
public String httpSecretKey;
@Value("${file_server_addr}")
public String fileServerAddr;
@RequestMapping("/index")
public String index(){
return "index" ;
}
/**
* 上傳到伺服器
*
* @param file
* @return
*/
@RequestMapping("/upload/file")
public String upload(MultipartFile file){
ResultData responseData = new ResultData();
try {
// 上傳到伺服器
String filepath = fastDFSClient.uploadFileWithMultipart(file);
// 設定訪檔案的Http地址. 有時效性.
String token = FastDFSClient.getToken(filepath, httpSecretKey);
System.out.println("上傳token:"+token);
System.out.println("上傳返回路徑path:"+filepath);
itemService.insertItem(new Item(file.getOriginalFilename(),filepath));
return "redirect:http://"+fileServerAddr+"/"+filepath+"?token="+token ;
} catch (Exception e) {
e.printStackTrace();
}
return null ;
}
/**
* 下載檔案
*
* @param filePath
* @param response
*/
@RequestMapping("/download/file")
public void downloadFile(String filePath, HttpServletResponse response) throws Exception {
try {
fastDFSClient.downloadFile(filePath, response);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
/**
* 刪除伺服器檔案
*
* @param filePath
*/
@RequestMapping("/delete/file")
public ResultData deleteFile(String filePath) throws Exception{
ResultData responseData = new ResultData();
Item item = new Item();
item.setUrl(filePath);
List<Item> items = itemService.selectItemList(item);
if(item!=null && items.size()>0){
itemService.selectItemById(items.get(0).getId());
}
try {
fastDFSClient.deleteFile(filePath);
} catch (Exception e) {
e.printStackTrace();
responseData.setSuccess(false);
responseData.setCode("下載檔案失敗!");
responseData.setMessage(e.getMessage());
}
return responseData;
}
/**
* 獲取訪問檔案的token
* @param filePath
* @return
*/
@RequestMapping("/get/token")
@ResponseBody
public ResultData getToken(String filePath)throws Exception{
ResultData responseData = new ResultData();
// 設定訪檔案的Http地址. 有時效性.
String token = FastDFSClient.getToken(filePath, httpSecretKey);
responseData.setToken(token);
responseData.setHttpUrl(fileServerAddr+"/"+filePath+"?"+token);
return responseData;
}
}