找回密码
 立即注册
首页 业界区 安全 文件存储微服务-阿里云OSS

文件存储微服务-阿里云OSS

魄柜 昨天 22:00
一、文件微服务

1.1 文件微服务

文件上传功能,这类比较通用的业务,可以抽取为一个 文件微服务 。在这个文件微服务中需要有如下功能:

  • 处理文件上传。
  • 上传文件的查询。
  • 文件的删除。
  • 垃圾文件的处理。
在开发时需要将这些附件上传保存到存储服务器(阿里云、七牛云、FastDFS、MinIO等)并将这些附件记录、可访问的文件地址存储到数据库中;因为在产品详情页查看时需要使用到。要能够通过产品id查询到对应的附件。
说明了这个文件微服务需要记录下来,是由哪些业务上传过来的数据;而且附件的数量也比较庞大。所以需要有对应的数据库来记录业务与附件的关系。
1.2 文件上传技术分析


  • 文件存储:存储具体的文件;可以采用阿里云OSS存储。
  • 文件记录:直接记录上传的文件信息到数据库表,baoxian-file数据库是专门存放文件、附件的数据库;其中有一个附件的表 tab_file在这个表中存储附件的信息即可。
上传的资料很有可能比较大(700MB, 1G, 3G);那么将这些大文件发送到 后端微服务的时候,很有可能因为网络不稳定而上传失败或者要重试多次,影响体验。可以将这些资料大文件分割为多个1M大小的文件多次上传到后端微服务,提高上传的成功率。
简单文件上传时序图:
1.png

大文件分片上传时序图:
2.png

上传资料大文件的时候;需要对文件至少3次以上的处理;分别处理:分片初始化、n次分片上传、分片合并;所以也将对应3个上传接口。
二、对象存储

2.1 对象存储介绍

对象存储服务(Object Storage Service)是一种数据存储和管理模型,用于存储和组织非结构化数据(文件:文本、图片、音频、视频),通常以对象(Object)的形式存储数据。每个对象通常包括数据本身、元数据(描述数据的信息),以及一个唯一的标识符。总的来说;就是存文件的。也一般会称OSS为存储服务器。

  • 方式一:可以存储到服务器所在硬盘。
  • 方式二:可以自己搭建存储服务器;比如:MinIO、FastDFS都是可自行搭建的分布式文件存储服务器。
  • 方式三:可以使用第三方,自己不用搭;直接用就行。比如:阿里云OSS(https://oss.console.aliyun.com/bucket)、华为云OSS、七牛云等。
2.2 简单文件上传

使用Java SDK发起OSS请求,需要配置访问凭证。具体如下:
  1. # 测试之前需要先设置系统环境变量;打开CMD 执行如下命令:
  2. set OSS_ACCESS_KEY_ID=你自己在阿里云上的AccessKey
  3. set OSS_ACCESS_KEY_SECRET=你自己在阿里云上的AccessKeySecret
  4. # 永久生效
  5. setx OSS_ACCESS_KEY_ID "%OSS_ACCESS_KEY_ID%"
  6. setx OSS_ACCESS_KEY_SECRET "%OSS_ACCESS_KEY_SECRET%"
  7. # 设置之后;可以通过如下命令查看是否设置成功. IDEA中要生效的话,可以重启IDEA
  8. echo %OSS_ACCESS_KEY_ID%
  9. echo %OSS_ACCESS_KEY_SECRET%
复制代码
引入maven依赖
  1. <dependency>
  2.   <groupId>org.springframework.boot</groupId>
  3.   spring-boot-starter-test</artifactId>
  4. </dependency>
复制代码
简单上传代码:
  1. package com.itheima.sfbx.file;
  2. import com.aliyun.oss.ClientException;
  3. import com.aliyun.oss.OSS;
  4. import com.aliyun.oss.OSSClientBuilder;
  5. import com.aliyun.oss.OSSException;
  6. import com.aliyun.oss.common.auth.CredentialsProviderFactory;
  7. import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
  8. import com.aliyun.oss.model.PutObjectRequest;
  9. import com.aliyun.oss.model.PutObjectResult;
  10. import org.junit.Test;
  11. import java.io.ByteArrayInputStream;
  12. import java.io.File;
  13. public class AliOSSUploadTest {
  14.     /**
  15.      * 简单上传
  16.      */
  17.     @Test
  18.     public void testSimpleUpload() throws Exception {
  19.         // Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
  20.         String endpoint = "https://oss-cn-beijing.aliyuncs.com";
  21.         // 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
  22.         EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
  23.         // 填写Bucket名称,例如examplebucket。
  24.         String bucketName = "baoxian-oss";
  25.         // 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
  26.         String objectName = "upload/pic/logo.png";
  27.         // 填写本地文件的完整路径,例如D:\\localpath\\examplefile.txt。
  28.         // 如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件。
  29.         String filePath= "E:\\pic\\logo.png";
  30.         // 创建OSSClient实例。
  31.         OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider);
  32.         try {
  33.             // 创建PutObjectRequest对象。
  34.             PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, new File(filePath));
  35.             // 如果需要上传时设置存储类型和访问权限,请参考以下示例代码。
  36.             // ObjectMetadata metadata = new ObjectMetadata();
  37.             // metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
  38.             // metadata.setObjectAcl(CannedAccessControlList.Private);
  39.             // putObjectRequest.setMetadata(metadata);
  40.             // 上传文件。
  41.             PutObjectResult result = ossClient.putObject(putObjectRequest);
  42.             System.out.println(result);
  43.         } catch (OSSException oe) {
  44.             System.out.println("Caught an OSSException, which means your request made it to OSS, "
  45.                     + "but was rejected with an error response for some reason.");
  46.             System.out.println("Error Message:" + oe.getErrorMessage());
  47.             System.out.println("Error Code:" + oe.getErrorCode());
  48.             System.out.println("Request ID:" + oe.getRequestId());
  49.             System.out.println("Host ID:" + oe.getHostId());
  50.         } catch (ClientException ce) {
  51.             System.out.println("Caught an ClientException, which means the client encountered "
  52.                     + "a serious internal problem while trying to communicate with OSS, "
  53.                     + "such as not being able to access the network.");
  54.             System.out.println("Error Message:" + ce.getMessage());
  55.         } finally {
  56.             if (ossClient != null) {
  57.                 ossClient.shutdown();
  58.             }
  59.         }
  60.     }
  61. }
复制代码
上传成功:
3.png

2.3 分片文件上传

大文件上传面临网络中断风险和传输时间过长的挑战。分片上传通过将文件分割为多个小分片并发传输,提供断点续传能力和传输性能优化,有效应对网络不稳定环境下的文件传输需求。
在上传大文件(超过5 GB)到OSS的过程中,如果出现网络中断、程序异常退出等问题导致文件上传失败,您需要使用分片上传的方式上传大文件。分片上传通过将待上传的大文件分成多个较小的碎片(Part),充分利用网络带宽和服务器资源并行上传多个Part,加快上传完成时间,并在Part上传完成之后调用CompleteMultipartUpload接口将这些Part组合成一个完整的Object。
使用场景:

  • 大文件加速上传。当文件大小超过5 GB时,使用分片上传可实现并行上传多个Part以加快上传速度。
  • 网络环境较差。网络环境较差时,建议使用分片上传。当出现上传失败的时候,您仅需重传失败的Part。
  • 暂停和恢复上传:分片上传任务没有过期时间。您可以随时暂停和恢复分片上传,直到完成或取消分片上传。
  • 文件大小不确定。可以在需要上传的文件大小还不确定的情况下开始上传,这种场景在视频监控等行业应用中比较常见。
分片文件上传流程说明:
4.png


  • 将待上传文件按照一定大小进行分片。
  • 使用InitiateMultipartUpload接口初始化一个分片上传任务。
  • 使用UploadPart接口上传分片。
    文件切分成Part之后,文件顺序是通过上传过程中指定的partNumber来确定,所以您可以并发上传这些碎片。并发数并非越多越快,请结合自身网络状况和设备负载综合考虑。如果您希望终止上传任务,可调用AbortMultipartUpload接口,成功上传的Part会一并删除。
  • 使用CompleteMultipartUpload接口将Part组合成一个Object。
使用限制:
单个文件的大小:不超过48.8TB。
分片数量:1~10,000个。
单个分片大小:最小值为100KB,最大值为5GB。最后一个分片的大小允许小于100KB。
分片上传代码:
  1. /**
  2. * 分片上传
  3. */
  4. @Test
  5. public void testMultiPartUpload() throws Exception {
  6.     // Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
  7.     String endpoint = "https://oss-cn-beijing.aliyuncs.com";
  8.     // 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
  9.     EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
  10.     // 填写Bucket名称,例如examplebucket。
  11.     String bucketName = "baoxian-oss";
  12.     // 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
  13.     String objectName = "upload/file/"+ UUID.randomUUID()+".pdf";
  14.     // 填写本地文件的完整路径,例如D:\\localpath\\examplefile.txt。
  15.     // 如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件。
  16.     String filePath= "E:\\file\\Java.pdf";
  17.     // 创建OSSClient实例。
  18.     OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider);
  19.     try {
  20.         //1、初始化上传
  21.         InitiateMultipartUploadRequest initiateMultipartUploadRequest = new InitiateMultipartUploadRequest(bucketName, objectName);
  22.         InitiateMultipartUploadResult initiateMultipartUploadResult = ossClient.initiateMultipartUpload(initiateMultipartUploadRequest);
  23.         String uploadId = initiateMultipartUploadResult.getUploadId();
  24.         System.out.println("分片上传初始化完成;uploadId:" + uploadId);
  25.         //2、分片上传文件
  26.         //2.1、读取要上传的文件
  27.         File file = new File(filePath);
  28.         //2.2、计算要分片上传的次数;每个分片大小设置为1M,计算要上传多少次
  29.         //单个分片文件大小;1MB
  30.         long partSize = 1024 * 1024;
  31.         //文件总长度
  32.         long fileLength = file.length();
  33.         //分为几个分片
  34.         int partCount  = (int) (fileLength / partSize);
  35.         if (fileLength % partSize != 0) {
  36.             partCount++;
  37.         }
  38.         System.out.println("总共分为:" + partCount + "个分片");
  39.         //2.3、循环上传分片
  40.         //记录每次每个分片上传之后的eTag
  41.         List<PartETag> partETags = new ArrayList<PartETag>();
  42.         for (int i = 0; i < partCount; i++) {
  43.             //当前分片的文件起始位置
  44.             long startPos = i * partSize;
  45.             //创建分片请求对象
  46.             UploadPartRequest uploadPartRequest = new UploadPartRequest();
  47.             //桶名称
  48.             uploadPartRequest.setBucketName(bucketName);
  49.             //上传的文件名
  50.             uploadPartRequest.setKey(objectName);
  51.             uploadPartRequest.setUploadId(uploadId);
  52.             //分片号
  53.             uploadPartRequest.setPartNumber(i + 1);
  54.             //设置当前分片文件内容
  55.             FileInputStream fileInputStream = new FileInputStream(file);
  56.             fileInputStream.skip(startPos);
  57.             uploadPartRequest.setInputStream(fileInputStream);
  58.             //当前这个分片的大小;但是最后一个分片大小可能是不到1M的;所以需要处理
  59.             long currentPartSize = (i+1==partCount)?(fileLength-startPos):partSize;
  60.             uploadPartRequest.setPartSize(currentPartSize);
  61.             //上传分片
  62.             UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);
  63.             System.out.println("Part#" + uploadPartRequest.getPartNumber() + " ETag:" + uploadPartResult.getETag());
  64.             //记录 etag
  65.             partETags.add(uploadPartResult.getPartETag());
  66.         }
  67.         //3、完成分片上传;合并(阿里云端合并)
  68.         CompleteMultipartUploadRequest completeMultipartUploadRequest
  69.                 = new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);
  70.         ossClient.completeMultipartUpload(completeMultipartUploadRequest);
  71.         System.out.println("分片上传完成!");
  72.     } catch (OSSException oe) {
  73.         System.out.println("Caught an OSSException, which means your request made it to OSS, "
  74.                 + "but was rejected with an error response for some reason.");
  75.         System.out.println("Error Message:" + oe.getErrorMessage());
  76.         System.out.println("Error Code:" + oe.getErrorCode());
  77.         System.out.println("Request ID:" + oe.getRequestId());
  78.         System.out.println("Host ID:" + oe.getHostId());
  79.     } catch (ClientException ce) {
  80.         System.out.println("Caught an ClientException, which means the client encountered "
  81.                 + "a serious internal problem while trying to communicate with OSS, "
  82.                 + "such as not being able to access the network.");
  83.         System.out.println("Error Message:" + ce.getMessage());
  84.     } finally {
  85.         if (ossClient != null) {
  86.             ossClient.shutdown();
  87.         }
  88.     }
  89. }
复制代码
上传成功:
5.png

6.png


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册