Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

基于阿里云 OSS 文件上传统一服务 #5

Open
tsui66 opened this issue May 15, 2021 · 0 comments
Open

基于阿里云 OSS 文件上传统一服务 #5

tsui66 opened this issue May 15, 2021 · 0 comments
Labels

Comments

@tsui66
Copy link
Owner

tsui66 commented May 15, 2021

当一个相同的项目功能存在于三个以上的项目的时候,我们就应该重新考虑是否该把他独立成单服务,比如我们最常见的阿里云 OSS 文件上传服务。本文就「基于阿里云 OSS 文件上传统一服务」相对简单的工程做一个介绍。效率工程的探索我们应该跳出代码、框架聚焦于约定,这些实现都需要多方配合,如前端、后端以及运维人员。本文仅提供参考,细节上会有很多瑕疵。你可以从本文学到:

  • 阿里云 OSS STS 临时授权访问机制实现
  • 前后端组件化思路
  • 代码工程实现

STS 原理

阿里云 OSS 可以通过阿里云 STS(Security Token Service)进行临时授权访问。通过 STS,您可以为第三方应用或子用户(即用户身份由您自己管理的用户)颁发一个自定义时效和权限的访问凭证。

img

服务实现

凭证服务

// 创建临时 oss policy, 用户客户端直传凭证
  createSTSPolicy(expiration, accessKeyId, accessKeySecret, region, bucket) {
    const dirPath = `${bucket}/`;
    let policyString = {
      expiration, // 设置该Policy的失效时间,超过这个失效时间之后,就没有办法通过这个policy上传文件了
      conditions: [
        [ 'content-length-range', 0, 1048576000 ],
        [ 'starts-with', '$key', dirPath ],
      ],
    };
    policyString = JSON.stringify(policyString);
    const policy = new Buffer(policyString).toString('base64');
    const signature = crypto.createHmac('sha1', accessKeySecret).update(policy).digest('base64');
    const new_multipart_params = {
      OSSAccessKeyId: accessKeyId,
      host: `https://${bucket}.${region}`,
      policy,
      signature,
      expiration,
      startsWith: dirPath,
    };
    return new_multipart_params;
  }
// 假设调用该服务 API 为: http://127.0.0.1:7001/api/oss/credentials?bucket=xxx
// bucket 一个消费方服务对应一个

使用方式

import { Injectable, HttpService } from '@nestjs/common';
import { ConfigService } from 'nestjs-config';
@Injectable()
export class OssService {
  constructor(
    private readonly httpService: HttpService,
    private readonly configService: ConfigService
  ) {}
  async findAll(): Promise<any> {
    const res = await this.httpService.get(`http://127.0.0.1:7001/api/oss/credentials?bucket=workshop`).toPromise();
    return res.data;
  }
}
// 返回值说明, 以下字段阿里云文档都有说明,传送门在文末「引用」
{
    "code": 200,
    "data": {
        "OSSAccessKeyId": "STS.NTr85byvDqhXVNYej3HASP7v8",
        "host": "https://${bucket}.oss-cn-hangzhou.aliyuncs.com",
        "policy": "eyJleHBpcmF0aW9uIjoiMjAyMS0wNS0xNVQxMDoxMjoyNVoiLCJjb25kaXRpb25zIjpbWyJjb250ZW50LWxlbmd0aC1yYW5nZSIsMCwxMDQ4NTc2MDAwXSxbInN0YXJ0cy13aXRoIiwiJGtleSIsImRjLXdvcmtzaG9wLyJdXX0=",
        "signature": "WpkqHH4ori4NI1Du1U6lV37xcWI=",
        "expiration": "2021-05-15T10:12:25Z",
        "startsWith": "${bucket}/",
        "SecurityToken": "CAIS/QF1q6Ft5B2yfSjIr5fHc4/WlKllxqqzVGjogWpmRM5/v/Ld1Tz2IHhIfXdtB+wYsv8+nmxS6/oTlqp6U4cdtbUh+TU3vPpt6gqET9frma7ctM4p6vCMHWyUFGSIvqv7aPn4S9XwY+qkb0u++AZ43br9c0fJPTXnS+rr76RqddMKRAK1QCNbDdNNXGtYpdQdKGHaOITGUHeooBKJXBMx5lIj0D4it/Xvk5PM0HeE0g2mkN1yjp/qP52pY/NrOJpCSNqv1IR0DPGZjHcOtkYWqvst3fYYo2qb78vuCl1Q8giANPHP7tpsIQl2a643AadYq+Lmkvl1qhwBQyxuTeUmGoABaRdUV6l+4Ec/rgFXFl7vVvrLzmjukcGoSNkdxH3Eee9UfZvfruEcciRXy6vQAM8rMjeFsNUYIvtBmW5W3OztoG3TCscQAYH0x7YGCF/dygdnGjh4sD67C9S3B0R0fVD5LI1OQPbQd+JR1ePjAJfPpDrjSaBOvZX+fEsnPr8eLU4="
    },
    "message": "ok"
}

前端实现

 async ({ filename, file, onSuccess, onError }) => {
    const { data } = await getOssCredentials();
    const { host, policy, signature, startsWith, OSSAccessKeyId, SecurityToken } = data;

    const key = `${
      startsWith + String(new Date().getTime()) + parseInt(String(Math.random() * 100), 10)
    }.png`;
    const formdata = new FormData();
    formdata.append('name', filename);
    formdata.append('key', key);
    formdata.append('policy', policy);
    formdata.append('OSSAccessKeyId', OSSAccessKeyId);
    formdata.append('success_action_status', 200);
    formdata.append('Signature', signature);
    formdata.append('x-oss-security-token', SecurityToken);
    formdata.append('file', file);

    request.post(`${host}`, { body: formdata }).then(() => {
      const url = `${host}/${key}`; // url 即为文件链接
      const { onChange } = this.props;
      onSuccess({ url });
      if (onChange) {
        onChange(url, file);
      }
    }, onError);
  };

引用

@tsui66 tsui66 changed the title 基于阿里云 OSS 文件上传统一服务 [WIP]基于阿里云 OSS 文件上传统一服务 May 15, 2021
@tsui66 tsui66 added the STS aliyun oss sts label May 15, 2021
@tsui66 tsui66 changed the title [WIP]基于阿里云 OSS 文件上传统一服务 基于阿里云 OSS 文件上传统一服务 May 15, 2021
@tsui66 tsui66 closed this as completed May 15, 2021
@tsui66 tsui66 reopened this May 15, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant