0%

oss 实现文件上传

1. 文件存储

1.1 普通文件存储方式和问题

单点项目服务上传方式如图1所示,即浏览器上传文件生成文件流最终写在服务器指定目录文件下。但是当在分布式的情况下时,如图2所示,由于文件可能上传到某台服务器上,因此有些服务器缺少对应的上传文件,导致无法访问到上传的文件。

图1

图2

1.2 公共文件中心存储

如图3所示,我们将上传的文件集中存储在文件存储中心,文件存储中心可以是自建服务器中心,也可以利用第三方云存储,比如阿里云对象存储。文章后面我们采用阿里云对象存储 OSS 来实现文件的上传功能。

图3

2. OSS 文件上传

2.1 阿里云对象存储-普通上传方式

对于普通上传方式,用户在浏览器点击上传文件,然后生成文件流传输到后台,然后交由后台使用 OSS 登录秘钥等信息实现将文件上传到 OSS 文件存储中心。因为上传文件传输的步骤需要经过应用服务器处理,所以这种方式有一个很大的缺点就是应用服务器容易遇到性能瓶颈。

普通上传方式

2.2 阿里云对象存储-服务端签名后直传

阿里云对象存储提供给我们另外一种解决方案,如下图,用户首先向服务获取 OSS 访问秘钥等信息,然后直接由用户在浏览器实现将文件上传到 OSS 文件存储中心,以此避免后台应用服务器进行文件数据传输和上传等步骤。

服务端签名后直传

3. 普通 Java 实现

  1. 创建 Bucket,创建地址点击进入,创建完成后即可获取查看 Endpoint 信息;

  2. 创建 RAM 访问控制子用户,得到 AccessKey 和 accessKeySecret;

  3. 创建 Maven 工程,引入依赖:

    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.10.2</version>
    </dependency>
  4. 创建如下测试方法,即可将对应路径中的文件上传到 OSS 文件中心:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    @Test
    public void testOssUpload() throws FileNotFoundException {
    // Endpoint以广州为例,其它Region请按实际情况填写。
    String endpoint = "https://oss-cn-guangzhou.aliyuncs.com";

    // 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录RAM控制台创建RAM账号。
    String accessKeyId = "xxxx";
    String accessKeySecret = "xxxx";
    String bucketName = "gulimall-oss-shotozheng";

    // 创建OSSClient实例。
    OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

    // <yourObjectName>上传文件到OSS时需要指定包含文件后缀在内的完整路径,例如abc/efg/123.jpg。
    String objectName = "imgs/aa.jpeg";

    // 上传文件流
    InputStream inputStream = new FileInputStream("C:\\Users\\admin\\Pictures\\Camera Roll\\f.jpeg");
    ossClient.putObject(bucketName, objectName, inputStream);

    // 关闭OSSClient。
    ossClient.shutdown();

    log.info("上传完成...");

    }
  5. 关于更新信息,可以查看官网

4. Spring Cloud 实现

  1. 创建 Bucket,创建地址点击进入,创建完成后即可获取查看 Endpoint 信息;

  2. 创建 RAM 访问控制子用户,得到 AccessKey 和 accessKeySecret;

  3. POM 文件引入依赖:

    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alicloud-oss</artifactId>
    <version>2.1.0.RELEASE</version>
    </dependency>
  4. application.yml 中配置如下信息:

    1
    2
    3
    4
    5
    6
    7
    8
    spring:
    cloud:
    alicloud:
    access-key: xxx
    secret-key: xxx
    oss:
    endpoint: oss-cn-guangzhou.aliyuncs.com
    bucket: gulimall-oss-shotozheng
  5. 创建测试类,如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    @Slf4j
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class OssUploadTests {

    @Autowired
    private OSSClient ossClient;

    @Value("${spring.cloud.alicloud.oss.bucket}")
    private String bucket;

    @Test
    public void saveFile() throws FileNotFoundException {
    String objectName = "imgs/bb.jpeg";
    InputStream inputStream = new FileInputStream("C:\\Users\\admin\\Pictures\\Camera Roll\\f.jpeg");
    ossClient.putObject(bucket, objectName, inputStream);

    // 关闭OSSClient。
    ossClient.shutdown();

    log.info("上传完成...");
    }
    }
  6. 服务端签名后上传,前台访问后台获取访问 OSS 上传签名之后才能直接在前端上传文件到 OSS 文件中,如下是获取服务端签名代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    @Slf4j
    @RestController
    public class OssController {

    @Resource
    private OSSClient ossClient;

    @Value("${spring.cloud.alicloud.access-key}")
    private String accessId;

    @Value("${spring.cloud.alicloud.oss.endpoint}")
    private String endpoint;

    @Value("${spring.cloud.alicloud.oss.bucket}")
    private String bucket;

    @RequestMapping("/oss/policy")
    public Map<String, String> policy() {
    // host的格式为 bucketname.endpoint
    String host = "https://" + bucket + "." + endpoint;
    // 用户上传文件时指定的前缀。
    String prefix = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
    String dir = prefix + "/";
    try {
    long expireTime = 30;
    long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
    Date expiration = new Date(expireEndTime);
    // PostObject请求最大可支持的文件大小为5 GB,即CONTENT_LENGTH_RANGE为5*1024*1024*1024。
    PolicyConditions policyConds = new PolicyConditions();
    policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
    policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);

    String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
    byte[] binaryData = postPolicy.getBytes("utf-8");
    String encodedPolicy = BinaryUtil.toBase64String(binaryData);
    String postSignature = ossClient.calculatePostSignature(postPolicy);

    Map<String, String> respMap = new LinkedHashMap<>();
    respMap.put("accessid", accessId);
    respMap.put("policy", encodedPolicy);
    respMap.put("signature", postSignature);
    respMap.put("dir", dir);
    respMap.put("host", host);
    respMap.put("expire", String.valueOf(expireEndTime / 1000));

    return respMap;
    } catch (Exception e) {
    log.info(e.getMessage());
    } finally {
    ossClient.shutdown();
    }
    return null;
    }
    }

    访问结果如下图所示:

    获取签名结果

------ 本文结束------