金歌 发表于 2024-5-16 17:01:08

Java实现Fast DFS、服务器、OSS上传

支持Fast DFS、服务器、OSS等上传方式
先容

在实际的业务中,可以根据客户的需求设置差别的文件上传需求,支持普通服务器上传+分布式上传(Fast DFS)+云服务上传OSS(OSS)
软件架构

为了方便演示使用,本项目使用的是前后端不分离的架构
前端:Jquery.uploadFile
后端:SpringBoot
前期准备:FastDFS、OSS(华为)、服务器
实现逻辑

通过 application 配置对上传文件举行一个自定义配置,从而摆设在差别客户环境可以自定义选择方式。
优点:

[*]一键切换;
[*]支持当前主流方式;
缺点:

[*]迁移数据难度增长:因为体现FileID在对象存储和服务器上传都是天生的UUID,而FastDFS是返回存取ID,当需要迁移的时间,通过脚本可以快速将FastDFS的数据迁移上云,因为存储ID可以共用。但是对象存储和服务器上传的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 标识
   * @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(""+storePath.getFullPath());
            return ResultBean.success(storePath.getPath());
      }catch (Exception e){
            log.info(""+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 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();
            // 创建ObsClient实例
            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(""+e.getErrorMessage());
            return ResultBean.error(e.getErrorMessage());
      }catch (Exception e){
            log.info(""+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(""+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("//", "/");
                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(""+e.getMessage());

      }
      return group1s;
    }

    /**
   * 下载文件对象存储
   * @param fileId 文件名
   * @return
   */
    private static byte[] downloadFileByHuaweiObject(String fileId, String huaweiEndPoint,String huaweiobsAk,String huaweiobsSk,
                                                   String bucketName){
      byte[] bytes =null;
      try {
            // 创建ObsClient实例
            ObsClient obsClient = new ObsClient(huaweiobsAk, huaweiobsSk, huaweiEndPoint);
            // 构造GetObjectRequest请求
            GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, fileId);
            // 执行下载操作
            ObsObject obsObject = obsClient.getObject(getObjectRequest);
            bytes = inputStreamToByteArray(obsObject.getObjectContent());
            // 关闭OBS客户端
            obsClient.close();
            return bytes;
      }catch (ObsException e){
            log.info(""+e.getErrorMessage());
      }catch (Exception e) {
            log.info(""+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;
      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;
      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(""+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("//", "/");
            result=deleteByOrigin(path2);
      }
      return result;
    }
    private static boolean deleteByOrigin(String fileName) {
      File file = new File(fileName);
      // 如果文件路径所对应的文件存在,并且是一个文件,则直接删除
      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(""+e.getMessage());
            return false;
      }
      return true;
    }

    /**
   * 生成fileId
   * @param fileName
   * @return
   */
    private static String renameToUUID(String fileName) {
      return UUID.randomUUID() + "." + fileName.substring(fileName.lastIndexOf(".") + 1);
    }

    /**
   * 删除文件对象存储
   * @param fileId 文件名
   * @return
   */
    private static boolean deleteFileByHuaweiObject(String fileId, String huaweiEndPoint,String huaweiobsAk,String huaweiobsSk,
                                                    String bucketName){
      try {
            // 创建ObsClient实例
            ObsClient obsClient = new ObsClient(huaweiobsAk, huaweiobsSk, huaweiEndPoint);
            // 构造GetObjectRequest请求
            DeleteObjectRequest getObjectRequest = new DeleteObjectRequest(bucketName, fileId);

            // 执行删除操作
            obsClient.deleteObject(getObjectRequest);
            // 关闭OBS客户端
            obsClient.close();
      }catch (ObsException e){
            log.info(""+e.getErrorMessage());
      }catch (Exception e) {
            log.info(""+e.getMessage());
      }
      return true;
    }

    /**
   * 文件数据输出(image)
   * @param fileId
   * @param bytes
   * @param res
   */
    public static void fileDataOut(String fileId, byte[] bytes, HttpServletResponse res){

      String[] prefixArray = fileId.split("\\.");
      String prefix=prefixArray;
      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();
                }
            }
      }
    }

}参考资料

假设个人实战使用可以采用docker快速安装,但是未安装过FastDFS建议普通安装摆设,了解一下tracker和storage的使用,以及摆设搭建集群。
FastDFS普通安装摆设:https://www.cnblogs.com/chenliugou/p/15322389.html
FasdDFS docker安装摆设:https://blog.csdn.net/weixin_44621343/article/details/117825755?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0-117825755-blog-127896984.235v43pc_blog_bottom_relevance_base6&spm=1001.2101.3001.4242.1&utm_relevant_index=3
OpenFeign和FastDFS的类冲突:https://www.cnblogs.com/chenliugou/p/18113183
Gitee地址:https://gitee.com/chen-liugou/file/new/master?readme=true

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Java实现Fast DFS、服务器、OSS上传