Java實(shí)現(xiàn)Fast DFS、服務(wù)器、OSS上傳功能
支持Fast DFS、服務(wù)器、OSS等上傳方式
介紹
在實(shí)際的業(yè)務(wù)中,可以根據(jù)客戶的需求設(shè)置不同的文件上傳需求,支持普通服務(wù)器上傳+分布式上傳(Fast DFS)+云服務(wù)上傳OSS(OSS)
軟件架構(gòu)
為了方便演示使用,本項(xiàng)目使用的是前后端不分離的架構(gòu)
前端:Jquery.uploadFile
后端:SpringBoot
前期準(zhǔn)備:FastDFS、OSS(華為)、服務(wù)器
實(shí)現(xiàn)邏輯
通過(guò) application 配置對(duì)上傳文件進(jìn)行一個(gè)自定義配置,從而部署在不同客戶環(huán)境可以自定義選擇方式。
優(yōu)點(diǎn):
- 一鍵切換;
- 支持當(dāng)前主流方式;
缺點(diǎn):
- 遷移數(shù)據(jù)難度增加:因?yàn)楸硎綟ileID在對(duì)象存儲(chǔ)和服務(wù)器上傳都是生成的UUID,而FastDFS是返回存取ID,當(dāng)需要遷移的時(shí)候,通過(guò)腳本可以快速將FastDFS的數(shù)據(jù)遷移上云,因?yàn)榇鎯?chǔ)ID可以共用。但是對(duì)象存儲(chǔ)和服務(wù)器上傳的UUID無(wú)法被FastDFS使用,增加遷移成本
核心代碼
package com.example.file.util; import com.example.file.common.ResultBean; import com.github.tobato.fastdfs.domain.StorePath; import com.github.tobato.fastdfs.proto.storage.DownloadByteArray; import com.github.tobato.fastdfs.service.FastFileStorageClient; import com.obs.services.ObsClient; import com.obs.services.exception.ObsException; import com.obs.services.model.DeleteObjectRequest; import com.obs.services.model.GetObjectRequest; import com.obs.services.model.ObsObject; import com.obs.services.model.PutObjectResult; import io.micrometer.common.util.StringUtils; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.mock.web.MockMultipartFile; import org.springframework.web.multipart.MultipartFile; import java.io.*; import java.util.Objects; import java.util.UUID; @Slf4j public class FileUtil { /** * * @param file 文件 * @param uploadFlag 標(biāo)識(shí) * @param uploadPath 上傳路徑 * @param endPoint 域名 * @param ak ak * @param sk sk * @param bucketName 桶名字 * @return fileId 用于下載 */ public static ResultBean uploadFile(MultipartFile file, String uploadFlag, String uploadPath, FastFileStorageClient fastFileStorageClient, String endPoint, String ak, String sk, String bucketName) { if (StringUtils.isBlank(uploadFlag)){ ResultBean.error("uploadFlag is null"); } switch (uploadFlag){ case "fastDFS": return uploadFileByFastDFS(file,fastFileStorageClient); case "huaweiOOS": return uploadFileByHuaweiObject(file, endPoint, ak, ak, bucketName); case "server": default: return uploadFileByOrigin(file,uploadPath); }} /** * 上傳文件fastDFS * @param file 文件名 * @return */ private static ResultBean uploadFileByFastDFS(MultipartFile file,FastFileStorageClient fastFileStorageClient){ Long size=file.getSize(); String fileName=file.getOriginalFilename(); String extName=fileName.substring(fileName.lastIndexOf(".")+1); InputStream inputStream=null; try { inputStream=file.getInputStream(); //1-上傳的文件流 2-文件的大小 3-文件的后綴 4-可以不管他 StorePath storePath=fastFileStorageClient.uploadFile(inputStream,size,extName,null); log.info("[uploadFileByFastDFS][FullPath]"+storePath.getFullPath()); return ResultBean.success(storePath.getPath()); }catch (Exception e){ log.info("[ERROR][uploadFileByFastDFS]"+e.getMessage()); return ResultBean.error(e.getMessage()); }finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 對(duì)象存儲(chǔ)上傳 * @param file 文件名 * @return */ private static ResultBean uploadFileByHuaweiObject(MultipartFile file, String huaweiEndPoint,String huaweiobsAk,String huaweiobsSk, String bucketName){ String fileName = file.getOriginalFilename(); fileName = renameToUUID(fileName); InputStream inputStream=null; try { inputStream=file.getInputStream(); // 創(chuàng)建ObsClient實(shí)例 ObsClient obsClient = new ObsClient(huaweiobsAk, huaweiobsSk, huaweiEndPoint); PutObjectResult result = obsClient.putObject(bucketName, fileName, inputStream); obsClient.close(); return ResultBean.success(fileName); }catch (ObsException e){ log.info("[ERROR][uploadFileByHuaweiObject]"+e.getErrorMessage()); return ResultBean.error(e.getErrorMessage()); }catch (Exception e){ log.info("[ERROR][uploadFileByHuaweiObject]"+e.getMessage()); return ResultBean.error(e.getMessage()); }finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 上傳文件原本方法 * @param file 文件名 * @return */ private static ResultBean uploadFileByOrigin(MultipartFile file,String uploadPath){ String fileName = file.getOriginalFilename(); fileName = renameToUUID(fileName); File targetFile = new File(uploadPath); if (!targetFile.exists()) { targetFile.mkdirs(); } FileOutputStream out = null; try { out = new FileOutputStream(uploadPath + fileName); out.write(file.getBytes()); } catch (IOException e) { log.info("[ERROR][uploadFileByOrigin]"+e.getMessage()); return ResultBean.error(e.getMessage()); }finally { try { out.flush(); } catch (IOException e) { e.printStackTrace(); } try { out.close(); } catch (IOException e) { e.printStackTrace(); } } return ResultBean.success(fileName); } /** * 下載 * @return */ public static byte[] downloadFile(String fileId,String uploadFlag,String uploadPath ,FastFileStorageClient fastFileStorageClient, String group, String endPoint,String ak,String sk, String bucketName) { byte[] result=null; switch (uploadFlag){ case "fastDFS": result =downloadFileByFastDFS(fileId,fastFileStorageClient,group); break; case "huaweiOOS": result =downloadFileByHuaweiObject(fileId, endPoint, ak, sk, bucketName); break; case "server": default: String path2 = uploadPath + fileId; path2 = path2.replace("http://", "/"); result=downloadFileByOrigin(path2); break; } return result; } /** * 下載文件fastDFS * @param fileId 文件名 * @return */ private static byte[] downloadFileByFastDFS(String fileId,FastFileStorageClient fastFileStorageClient, String group){ DownloadByteArray callback=new DownloadByteArray(); byte[] group1s=null; try { group1s = fastFileStorageClient.downloadFile(group, fileId, callback); }catch (Exception e){ log.info("[ERROR][downloadFileByFastDFS]"+e.getMessage()); } return group1s; } /** * 下載文件對(duì)象存儲(chǔ) * @param fileId 文件名 * @return */ private static byte[] downloadFileByHuaweiObject(String fileId, String huaweiEndPoint,String huaweiobsAk,String huaweiobsSk, String bucketName){ byte[] bytes =null; try { // 創(chuàng)建ObsClient實(shí)例 ObsClient obsClient = new ObsClient(huaweiobsAk, huaweiobsSk, huaweiEndPoint); // 構(gòu)造GetObjectRequest請(qǐng)求 GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, fileId); // 執(zhí)行下載操作 ObsObject obsObject = obsClient.getObject(getObjectRequest); bytes = inputStreamToByteArray(obsObject.getObjectContent()); // 關(guān)閉OBS客戶端 obsClient.close(); return bytes; }catch (ObsException e){ log.info("[ERROR][downloadFileByHuaweiObject]"+e.getErrorMessage()); }catch (Exception e) { log.info("[ERROR][downloadFileByHuaweiObject]"+e.getMessage()); } return bytes; } /** * * @param input * @return * @throws IOException */ private static byte[] inputStreamToByteArray(InputStream input) throws IOException { ByteArrayOutputStream output = new ByteArrayOutputStream(); byte[] buffer = new byte[4096]; int n = 0; while (-1 != (n = input.read(buffer))) { output.write(buffer, 0, n); } return output.toByteArray(); } /** * 下載文件 * @param fileId 文件名 * @return */ private static byte[] downloadFileByOrigin(String fileId){ File file =new File(fileId); InputStream inputStream=null; byte[] buff = new byte[1024]; byte[] result=null; BufferedInputStream bis = null; ByteArrayOutputStream os = null; try { os=new ByteArrayOutputStream(); bis = new BufferedInputStream(new FileInputStream(file)); int i = bis.read(buff); while (i != -1) { os.write(buff, 0, buff.length); i = bis.read(buff); } result=os.toByteArray(); os.flush(); } catch (Exception e) { log.info("[ERROR][downloadFile]"+e.getMessage()); }finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } return result; } /** * 刪除文件 * @param fileId 文件ID * @return 刪除失敗返回-1,否則返回0 */ public static boolean deleteFile(String fileId,String fastDFSFlag,FastFileStorageClient fastFileStorageClient, String group,String uploadPath) { boolean result=false; if (StringUtils.isNotBlank(fastDFSFlag)&& fastDFSFlag.trim().equalsIgnoreCase("true")){ result =deleteFileByFastDFS(fileId,fastFileStorageClient,group); }else { String path2 = uploadPath + fileId; path2 = path2.replace("http://", "/"); result=deleteByOrigin(path2); } return result; } private static boolean deleteByOrigin(String fileName) { File file = new File(fileName); // 如果文件路徑所對(duì)應(yīng)的文件存在,并且是一個(gè)文件,則直接刪除 if (file.exists() && file.isFile()) { if (file.delete()) { return true; } else { return false; } } else { return false; } } private static boolean deleteFileByFastDFS(String fileId,FastFileStorageClient fastFileStorageClient, String group) { try { String groupFieId=group+"/"+fileId; StorePath storePath = StorePath.praseFromUrl(groupFieId); fastFileStorageClient.deleteFile(storePath.getGroup(), storePath.getPath()); } catch (Exception e) { log.info("[ERROR][deleteFileByFastDFS]"+e.getMessage()); return false; } return true; } /** * 生成fileId * @param fileName * @return */ private static String renameToUUID(String fileName) { return UUID.randomUUID() + "." + fileName.substring(fileName.lastIndexOf(".") + 1); } /** * 刪除文件對(duì)象存儲(chǔ) * @param fileId 文件名 * @return */ private static boolean deleteFileByHuaweiObject(String fileId, String huaweiEndPoint,String huaweiobsAk,String huaweiobsSk, String bucketName){ try { // 創(chuàng)建ObsClient實(shí)例 ObsClient obsClient = new ObsClient(huaweiobsAk, huaweiobsSk, huaweiEndPoint); // 構(gòu)造GetObjectRequest請(qǐng)求 DeleteObjectRequest getObjectRequest = new DeleteObjectRequest(bucketName, fileId); // 執(zhí)行刪除操作 obsClient.deleteObject(getObjectRequest); // 關(guān)閉OBS客戶端 obsClient.close(); }catch (ObsException e){ log.info("[ERROR][deleteFileByHuaweiObject]"+e.getErrorMessage()); }catch (Exception e) { log.info("[ERROR][downloadFileByHuaweiObject]"+e.getMessage()); } return true; } /** * 文件數(shù)據(jù)輸出(image) * @param fileId * @param bytes * @param res */ public static void fileDataOut(String fileId, byte[] bytes, HttpServletResponse res){ String[] prefixArray = fileId.split("\\."); String prefix=prefixArray[1]; File file =new File(fileId); res.reset(); res.setCharacterEncoding("utf-8"); // res.setHeader("content-type", ""); res.addHeader("Content-Length", "" + bytes.length); if(prefix.equals("svg")) prefix ="svg+xml"; res.setContentType("image/"+prefix); res.setHeader("Accept-Ranges","bytes"); OutputStream os = null; try { // os = res.getOutputStream(); // os.write(bytes); // os.flush(); res.getOutputStream().write(bytes); } catch (IOException e) { System.out.println("not find img.."); } finally { if (os != null) { try { os.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
參考資料
假設(shè)個(gè)人實(shí)戰(zhàn)使用可以采用docker快速安裝,但是未安裝過(guò)FastDFS建議普通安裝部署,了解一下tracker和storage的使用,以及部署搭建集群。
FastDFS普通安裝部署:https://www.cnblogs.com/chenliugou/p/15322389.html
OpenFeign和FastDFS的類沖突:https://www.cnblogs.com/chenliugou/p/18113183
Gitee地址:https://gitee.com/chen-liugou/file/new/master?readme=true
到此這篇關(guān)于Java實(shí)現(xiàn)Fast DFS、服務(wù)器、OSS上傳功能的文章就介紹到這了,更多相關(guān)java Fast DFS上傳內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java實(shí)現(xiàn)整合文件上傳到FastDFS的方法詳細(xì)
- Java實(shí)現(xiàn)圖片上傳至FastDFS入門教程
- Java 客戶端操作 FastDFS 實(shí)現(xiàn)文件上傳下載替換刪除功能
- Java fastdfs客戶端實(shí)現(xiàn)上傳下載文件
- Java使用OSS實(shí)現(xiàn)上傳文件功能
- Java下載https文件并上傳阿里云oss服務(wù)器
- Java微信小程序oss圖片上傳的實(shí)現(xiàn)方法
- java實(shí)現(xiàn)上傳文件到oss(阿里云)功能示例
- java獲取網(wǎng)絡(luò)圖片上傳到OSS的方法
相關(guān)文章
SpringBoot集成screw實(shí)現(xiàn)數(shù)據(jù)庫(kù)文檔生成的代碼示例
數(shù)據(jù)庫(kù)設(shè)計(jì)文檔是項(xiàng)目技術(shù)文檔的重要組成部分,Screw 是一款開(kāi)源的數(shù)據(jù)庫(kù)文檔生成工具,它支持多種數(shù)據(jù)庫(kù)類型,并能生成豐富格式的文檔,本文將通過(guò)一個(gè)實(shí)際的例子,展示如何使用 Spring Boot 集成 Screw 生成數(shù)據(jù)庫(kù)設(shè)計(jì)文檔2024-07-07Java編程實(shí)現(xiàn)五子棋人人對(duì)戰(zhàn)代碼示例
這篇文章主要介紹了Java編程實(shí)現(xiàn)五子棋人人對(duì)戰(zhàn)代碼示例,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-11-11Java 如何在switch case語(yǔ)句中聲明變量
這篇文章主要介紹了Java 如何在switch case語(yǔ)句中聲明變量,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10Hadoop MultipleOutputs輸出到多個(gè)文件中的實(shí)現(xiàn)方法
這篇文章主要介紹了 Hadoop MultipleOutputs輸出到多個(gè)文件中的實(shí)現(xiàn)方法的相關(guān)資料,希望通過(guò)本文能幫助到大家,需要的朋友可以參考下2017-10-10Java編程實(shí)現(xiàn)遍歷兩個(gè)MAC地址之間所有MAC的方法
這篇文章主要介紹了Java編程實(shí)現(xiàn)遍歷兩個(gè)MAC地址之間所有MAC的方法,涉及Java針對(duì)MAC的遍歷獲取與字符串轉(zhuǎn)換相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11SpringBoot實(shí)現(xiàn)動(dòng)態(tài)端口切換黑魔法
這篇文章主要為大家詳細(xì)介紹了SpringBoot如何實(shí)現(xiàn)動(dòng)態(tài)端口切換黑魔法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-12-12Java的validation參數(shù)校驗(yàn)代碼實(shí)例
這篇文章主要介紹了Java的validation參數(shù)校驗(yàn)代碼實(shí)例,Validation參數(shù)校驗(yàn)是指在程序運(yùn)行中對(duì)傳進(jìn)來(lái)的參數(shù)進(jìn)行合法性檢查,以保證程序的正確性和安全性,需要的朋友可以參考下2023-10-10