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)邏輯
通過 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í)候,通過腳本可以快速將FastDFS的數(shù)據(jù)遷移上云,因?yàn)榇鎯?chǔ)ID可以共用。但是對(duì)象存儲(chǔ)和服務(wù)器上傳的UUID無法被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快速安裝,但是未安裝過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ù)庫文檔生成的代碼示例
數(shù)據(jù)庫設(shè)計(jì)文檔是項(xiàng)目技術(shù)文檔的重要組成部分,Screw 是一款開源的數(shù)據(jù)庫文檔生成工具,它支持多種數(shù)據(jù)庫類型,并能生成豐富格式的文檔,本文將通過一個(gè)實(shí)際的例子,展示如何使用 Spring Boot 集成 Screw 生成數(shù)據(jù)庫設(shè)計(jì)文檔2024-07-07
Java編程實(shí)現(xiàn)五子棋人人對(duì)戰(zhàn)代碼示例
這篇文章主要介紹了Java編程實(shí)現(xiàn)五子棋人人對(duì)戰(zhàn)代碼示例,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-11-11
Hadoop MultipleOutputs輸出到多個(gè)文件中的實(shí)現(xiàn)方法
這篇文章主要介紹了 Hadoop MultipleOutputs輸出到多個(gè)文件中的實(shí)現(xiàn)方法的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下2017-10-10
Java編程實(shí)現(xiàn)遍歷兩個(gè)MAC地址之間所有MAC的方法
這篇文章主要介紹了Java編程實(shí)現(xiàn)遍歷兩個(gè)MAC地址之間所有MAC的方法,涉及Java針對(duì)MAC的遍歷獲取與字符串轉(zhuǎn)換相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11
SpringBoot實(shí)現(xiàn)動(dòng)態(tài)端口切換黑魔法
這篇文章主要為大家詳細(xì)介紹了SpringBoot如何實(shí)現(xiàn)動(dòng)態(tài)端口切換黑魔法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-12-12
Java的validation參數(shù)校驗(yàn)代碼實(shí)例
這篇文章主要介紹了Java的validation參數(shù)校驗(yàn)代碼實(shí)例,Validation參數(shù)校驗(yàn)是指在程序運(yùn)行中對(duì)傳進(jìn)來的參數(shù)進(jìn)行合法性檢查,以保證程序的正確性和安全性,需要的朋友可以參考下2023-10-10

