preface

In daily development, the general process of uploading files on the client is as follows: The client sends a file to the server again by the server to file dump the special storage server or storage service of cloud computing vendors (such as ali cloud OSS), do a malpractice is the upload link server’s bandwidth, single-digit concurrent uploads can fill the bandwidth, thus resulting in a decline in the user experience. This problem can be avoided by transferring files directly from the client to a third-party storage service.

This document uses Ali Cloud OSS (Object Storage Service) as an example to describe the process of transferring files directly from a client to the OSS and provides a complete code demonstration.

The advantages and disadvantages

The biggest benefit of changing from “client-server-OSS” mode to “client-OSS” mode is that the uploading process to the server is eliminated, and the uploading process is more efficient and faster. (Compared with the bandwidth of a common server, the OSS bandwidth can be considered “almost unlimited”.)

Of course, this model also has a disadvantage, that is, it adds a lot of extra development work, mainly consisting of two parts:

(1) Add the code for generating and uploading OSS credentials on the server.

(2) The client added the code for obtaining and uploading OSS credentials from the server and adapted the code for direct transmission of OSS.

Overall, there are few architectural drawbacks to the direct pass model, aside from a little more development effort.

process

In fact, the process is very simple, consisting of two steps:

(1) The client sends a request to the server to obtain the certificate for direct transmission of OSS.

(2) The client uploads files to the OSS and carries the credential with it.

Logic and dismantling

For information on how to generate a credential (also known as a signature), read the official documentation (help.aliyun.com/document_de…) , but since the document was created early, it can be difficult for beginners to understand, so this article will show you the whole process hand in hand.

The whole “generate and upload OSS credentials” process actually does a few things:

(1) Upload credential authentication is provided by the policy, which is generated based on the privacy configuration.

(2) Since the upload process is not connected to the developer server, you can define various restrictions in the policy, such as maximum upload size, file name, etc.

(3) Convert the policy to the specified format.

Code implementation

Let’s consider implementing each step of the process before encapsulating the process code into functions.

OSS configuration

First, define the OSS configuration file. For details about the configuration items, see help.aliyun.com/document_de…

/** OSS configuration items */
const ossConfig = {
  bucket: 'xxxxxxxx'.accessKeyId: 'xxxxxxxx'.accessKeySecret: 'xxxxxxxx'./** Domain name bound with OSS */
  url: 'xxxxxxxx',}Copy the code

The policy content

For policy, there are many configuration items. We first consider generating a “write dead” pattern, and then optimize to pass in configuration items as function arguments. The following is a basic policy.

The period of validity

First define a validity period (in milliseconds), then the validity end time of the credential is “current time + validity period”, and finally convert the credential into an ISO time string format.

/** Valid duration: for example, 4 hours */
const timeout = 4 * 60 * 60 * 1000

/** Expiration time: current time + Valid time */
const expiration = new Date(Date.now() + timeout).toISOString()
Copy the code

The file name

The name of the file is recommended to use UUID (I habitually use the UUID with the short line removed) to avoid duplication.

import { v4 as uuidv4 } from 'uuid'

/** Random file name (remove the dash uUID) */
const filename = uuidv4().replace(/-/gu.' ')
Copy the code

It is recommended to divide files into different directories based on service modules. For example, if the file directory is used here, the complete OSS file path is as follows:

/** Directory name */
const dirname = 'file'

/** File path */
const key = dirname + '/' + filename
Copy the code

Note that the file path cannot start with a slash (OSS itself requires).

Together, the policy text is formed. Here is a basic format:

const policyText = {
  expiration: expiration,
  conditions: [['eq'.'$bucket', ossConfig.bucket],
    ['eq'.'$key', key],
  ],
}
Copy the code

Transformation of the policy

Once the policyText is converted to Base64 format, the required policy is created.

// Convert policyText to Base64 format
const policy = Buffer.from(JSON.stringify(policyText)).toString('base64')
Copy the code

Then the policy is signed using the OSS key and HmacSha1 algorithm.

import * as crypto from 'crypto'

// Use the HmacSha1 algorithm for signature
const signature = crypto.createHmac('sha1', ossConfig.accessKeySecret).update(policy, 'utf8').digest('base64')
Copy the code

Finally, the relevant fields in the above process are returned to the client, which is “upload credentials”.

Further analysis

This is a complete demonstration of the entire process, and we further analyze how to encapsulate it into a generic function.

(1) The validity period of the certificate can be defined according to different business modules, so it is made into function configuration items.

(2) The directory name can also be made into a configuration item.

(3) Policy has more configuration content (see help.aliyun.com/document_de…) , you can extract some of them as configuration items, for example, maximum volume allowed to upload.

The complete code

Here is the code for using the Nest. Js Web framework wrapped as a “service” from my online project (with some tweaks and tweaks) for your reference.

import { Injectable } from '@nestjs/common'
import * as crypto from 'crypto'
import { v4 as uuidv4 } from 'uuid'

export interface GenerateClientTokenConfig {
  /** Directory name */
  dirname: string

  /** Valid time, unit: hour */expiration? :number

  /** Maximum upload volume (unit: MB */)maxSize? :number
}

/** Direct transmission certificate */
export interface ClientToken {
  key: string
  policy: string
  signature: string
  OSSAccessKeyId: string
  url: string
}

export interface OssConfig {
  bucket: string
  accessKeyId: string
  accessKeySecret: string
  url: string
}

@Injectable(a)export class OssService {
  private readonly ossConfig: OssConfig

  constructor() {
    this.ossConfig = {
      bucket: 'xxxxxxxx'.accessKeyId: 'xxxxxxxx'.accessKeySecret: 'xxxxxxxx'./** Domain name bound with OSS */
      url: 'xxxxxxxx',}}/** * generate a call credential that can be used by clients to directly pass OSS **@param Config Configuration items * *@see * / / configure content (https://help.aliyun.com/document_detail/31988.html#title-6w1-wj7-q4e)
  generateClientToken(config: GenerateClientTokenConfig): ClientToken {
    /** Directory name */
    const dirname = config.dirname

    /** Valid time: 4 hours by default */
    const timeout = (config.expiration || 4) * 60 * 60 * 1000

    /** Upload maximum size, default is 100 MB */
    const maxSize = (config.maxSize || 100) * 1024 * 1024

    /** Random file name (remove the dash uUID) */
    const filename = uuidv4().replace(/-/gu.' ')

    /** File path */
    const key = dirname + '/' + filename

    /** Expiration time: current time + Valid time */
    const expiration = new Date(Date.now() + timeout).toISOString()

    const { bucket, url, accessKeyId } = this.ossConfig

    const policyText = {
      expiration: expiration,
      conditions: [['eq'.'$bucket', bucket],
        ['eq'.'$key', key],
        ['content-length-range'.0, maxSize],
      ],
    }

    // Convert policyText to Base64 format
    const policy = Buffer.from(JSON.stringify(policyText)).toString('base64')

    // Use the HmacSha1 algorithm for signature
    const signature = crypto.createHmac('sha1'.this.ossConfig.accessKeySecret).update(policy, 'utf8').digest('base64')

    return { key, policy, signature, OSSAccessKeyId: accessKeyId, url }
  }
}
Copy the code

After completing the above service methods, this method can then be called at the “controller” layer to distribute upload credentials that can be used by clients to directly upload files to OSS.