Spring Boot实现文件云存储-上篇

366 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第12天,点击查看活动详情

背景

图片和文件存储是每个项目必备的需求,早些年公司使用的是自建的FastDFS分布式进行存储,但是最近几年云技术发展迅速,出现很多云存储的技术方案,本文将详细讲解如何使用云存储存储文件。

简介

云存储是一种网上在线存储的模式,即把数据存放在通常由第三方托管的多台虚拟服务器,而非专属的服务器上。常见的云存储有阿里OSS、腾讯的COS、百度的CFS、Minio等。

优势

  • 持久性:云服务器服务可用性不低于99.995%,能够持久提供服务。
  • 安全:提供企业级多层次安全防护,包括服务端加密、客户端加密、防盗链、通过Bucket Policy 限制IP黑白名单访问、细粒度权限管控、日志审计、WORM特性等。多用户资源隔离机制,支持异 地容灾机制。
  • 成本:可通过多线BGP接入运营商骨干网线路,带宽资源充足,上行流量免费。 无需运维人员与托管费用,0成本运维。
  • 智能存储:提供多种数据处理能力,如图片处理、视频截帧、文档预览、图片场景识别。

本文将以阿里云的OSS为例,来讲解云存储。

阿里云OSS云存储实现方案

阿里云OSS文件存储实现方案可以分为两种方案:

  • 后端上传
  • 前端直连OSS,上传文件

后端上传

后端上传的业务流程

图片.png

说明:用户将文件上传到应用服务器,应用服务器在将文件上传到OSS

具体实现

pom文件引入相关jar包

<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>2.5.0</version>
</dependency>
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-java-sdk-core</artifactId>
    <version>3.5.1</version>
</dependency>

属性配置

新建oss.properties文件,文件内容如下:

oss.accessKeyId=##
oss.accessKeySecret##
oss.bucket=##
oss.endpoint=##
oss.bucketImageUrl=##

//属性实体类

@Component
@ConfigurationProperties(prefix = "oss")
@PropertySource("classpath:oss.properties")
public class OssProperties
{
    private String accessKeyId;

    private String accessKeySecret;

    private String bucket;

    private String endpoint;

    private String bucketImageUrl;

    private String fileMaxSize;
    
    //
 }   

说明:填写阿里云OSS配置相关信息,关于相关的属性说明请查看阿里云文档。

工具类

// 获取ossclient连接工具类

public class OssFactory
{
  private OssProperties ossProperties;
  
  private static OssFactory ossFactory;
 
  private OssFactory()
  {
      ossProperties = BeanManager.getBean(OssProperties.class);
  }
  
  public synchronized static OssFactory getInstance ()
  {
      if(ossFactory==null)
      {
          ossFactory = new OssFactory();
      }
      return ossFactory;
  }
 
  public OSSClient getClient()
  {
      ClientConfiguration conf = new ClientConfiguration();
      conf.setSupportCname(true);
      OSSClient client = new OSSClient(ossProperties.getEndpoint(), ossProperties.getAccessKeyId(),
              ossProperties.getAccessKeySecret(), conf);
      return client;
  }
  
  public String getFileUrl(String ossPath)
  {
      StringBuffer sb = new StringBuffer();
      sb.append("http://");
      sb.append(ossProperties.getBucket());
      sb.append(".");
      sb.append(ossProperties.getEndpoint());
      sb.append("/");
      sb.append(ossPath);

      return sb.toString();
  }
  
  public void closeClient(OSSClient client)
  {
      if (client != null)
      {
          client.shutdown();
      }
  }
}

//操作文件上传的工具类

public class OssUtils
{
    public static Logger logger = LoggerFactory.getLogger(OssUtils.class);
   
      public static String putFile(String ossBucket,String ossFileName,String filePath)
    {
        logger.info("Put oss file. ossBucket:" + ossBucket + " ,ossFileName:" + ossFileName);
        OSSClient ossClient =null;
        try
        {
            ossClient = OssFactory.getInstance().getClient();
            File uploadFile = new File(filePath);
            InputStream ins =new FileInputStream(uploadFile);
            ossClient.putObject(ossBucket, ossFileName, ins);
            return OssFactory.getInstance().getFileUrl(ossFileName);
        }
        catch(Exception e)
        {
            logger.error("Put file to oss error. filePath:" + filePath  + " ,ossBucket:" + ossBucket+" ,"
                    + "ossFileName:"+ossFileName, e);
            return "";
        }
        finally
        {
            if(ossClient!=null)
            {
                ossClient.shutdown();
            }
        }
    }
   
   
   
}

测试示例类

@RestController
@RequestMapping("/oss")
public class OssController
{
    private Logger logger =LoggerFactory.getLogger(OssController.class);
    
    @Autowired
    private OssProperties ossProperties;
    
    @RequestMapping("/upload")
    public String upload() throws IOException
    {
        String ossFileName = "zmp"+ File.separatorChar + "dish"+File.separatorChar+"testdish.jpg";
        OssUtils.putFile(ossProperties.getBucket(), ossFileName, "E:\\upload\\test.jpg");
        String ossFilePath = OssFactory.getInstance().getFileUrl(ossFileName);
        logger.info("uploadDishImg ossFilePath:" + ossFilePath);
        return ossFilePath;
    }
}

方案存在的缺点

  1. 首先文件传入到应用服务器,在由应用服务器上传到OSS,经过了两道传输,会导致传输时间增长。

  2. 消耗了应用服务器的带宽流量,如果上传的文件数量比较多,会大量占用应用服务器流量导致应用服务器性能下降。

  3. 消耗了应用服务器的带宽流量,会产生一定的费用,增加了额外的成本。

  4. 此方案需要特别注意:应用服务器与OSS连级一定要配置OSS的地址为内网地址,否则会产生额外流量。

解决办法

采用前端直连OSS上传文件,关于这种方案下一章将进行讲解。

总结

本文讲解了文件存储采用云存储的解决方案,如有问题,请随时反馈。