Практика прямой передачи файлов OSS (1): сервер

Node.js
Практика прямой передачи файлов OSS (1): сервер

предисловие

В повседневной разработке общий процесс загрузки файлов с клиента выглядит следующим образом: клиент отправляет файл на сервер, а сервер выгружает файл на выделенный сервер хранения или в службу хранения поставщика облачных вычислений (например, Alibaba). Cloud OSS). Одним из недостатков этого является то, что ссылка для загрузки занимает пропускную способность сервера, а одновременная загрузка однозначных цифр может заполнить пропускную способность, что приведет к ухудшению пользовательского опыта. Этой проблемы можно избежать, напрямую загружая файлы с клиента в стороннее хранилище. ​

В этой статье в качестве примера используется Alibaba Cloud OSS (служба хранилища объектов), чтобы подробно описать общий процесс прямой передачи файлов от клиента к OSS, и представлена ​​полная демонстрация кода. ​

Преимущества и недостатки

При переходе от режима передачи «клиент-сервер-OSS» к режиму «клиент-OSS» самым большим преимуществом является то, что этап загрузки сервера опущен, а эффективность загрузки выше и скорость выше (по сравнению с пропускной способностью общий сервер можно считать «почти неограниченным» по пропускной способности OSS). ​

Конечно, у этого режима есть и недостатки, то есть он добавляет много дополнительной работы по развитию, в основном включающей 2 части: ​

(1) На стороне сервера добавляется код для создания и загрузки учетных данных OSS.

(2) Клиент добавляет код для получения и загрузки учетных данных OSS с сервера и адаптируется к прямому OSS. ​

В целом режим прямой передачи почти не имеет недостатков с архитектурной точки зрения, за исключением немного большей нагрузки на разработку. ​

Процесс

На самом деле весь процесс очень прост и состоит из двух шагов: ​

(1) Клиент отправляет запрос на сервер для получения сертификата для прямой передачи OSS.

(2) Клиент загружает файл в OSS и несет сертификат. ​

логическая разборка

О том, как генерировать учетные данные (также называемые подписями), вы можете прочитать в официальной документации (help.aliyun.com/document_…), но так как документ был создан рано, новичкам сложно его понять.Эта статья покажет вам весь процесс шаг за шагом. ​

Весь процесс «генерации и загрузки учетных данных OSS» на самом деле делает следующее:

(1) Сертификат загрузки аутентифицируетсяpolicyПри условии, сгенерируйте это на основе частной конфигурацииpolicy.

(2) Поскольку ссылка для загрузки отделена от сервера разработчика, вы можетеpolicyРазличные ограничения определены в , такие как максимальный размер загружаемого файла, имя файла и т. д.

(3) будетpolicyПреобразование в указанный формат. ​

Код

Сначала мы рассмотрим реализацию каждого шага процесса, а затем инкапсулируем код процесса в функции. ​

Конфигурация OSS

Сначала определите файл конфигурации OSS.Содержание элементов конфигурации можно найти в документации:help.aliyun.com/document_…

/** OSS 配置项 */
const ossConfig = {
  bucket: 'xxxxxxxx',
  accessKeyId: 'xxxxxxxx',
  accessKeySecret: 'xxxxxxxx',

  /** OSS 绑定的域名 */
  url: 'xxxxxxxx',
}

содержание политики

заpolicy, есть много элементов конфигурации, мы сначала рассмотрим создание «жестко запрограммированного» режима, а затем оптимизируем его для передачи элементов конфигурации через параметры функции. Ниже приведены основныеpolicy. ​

Срок годности

Сначала определите действительную продолжительность (единица измерения: миллисекунды), затем действительное время истечения срока действия ваучера представляет собой «текущее время + действительная продолжительность», и, наконец, его необходимо преобразовать в формат строки времени ISO. ​

/** 有效时长:例如 4 小时 */
const timeout = 4 * 60 * 60 * 1000

/** 到期时间:当前时间 + 有效时间 */
const expiration = new Date(Date.now() + timeout).toISOString()

имя файла

Рекомендуется использовать UUID для имени файла (я обычно использую UUID с удаленными тире), чтобы избежать повторения. ​

import { v4 as uuidv4 } from 'uuid'

/** 随机文件名(去掉短横线的 uuid) */
const filename = uuidv4().replace(/-/gu, '')

Обычно рекомендуется разбивать файлы по разным каталогам в соответствии с разными бизнес-модулями, например, использовать здесьfileкаталог, то полный путь к файлу OSS: ​

/** 目录名称 */
const dirname = 'file'

/** 文件路径 */
const key = dirname + '/' + filename

должны знать о том,Путь к файлу не может начинаться с "/"(Требуется самим OSS). ​

Объединяя вышеперечисленное, мы формируемpolicyТекст, следующий основной формат: ​

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

политика конверсии

будетpolicyTextпревратиться вBase64После формата требуетсяpolicy.

// 将 policyText 转化为 Base64 格式
const policy = Buffer.from(JSON.stringify(policyText)).toString('base64')

тогда правильноpolicyПодпишите подпись по алгоритму HmacSha1 с помощью ключа OSS.

import * as crypto from 'crypto'

// 使用 HmacSha1 算法签名
const signature = crypto.createHmac('sha1', ossConfig.accessKeySecret).update(policy, 'utf8').digest('base64')

Наконец, соответствующие поля в описанном выше процессе возвращаются клиенту, то есть «загрузить учетные данные». ​

дальнейший анализ

Вышеприведенное полностью демонстрирует весь процесс, и далее мы анализируем, как инкапсулировать его в общую функцию. ​

(1) Срок действия сертификата может быть определен в соответствии с различными бизнес-модулями, поэтому он превращается в элемент конфигурации функции.

(2) Имя каталога также можно сделать элементом конфигурации.

(3)policyЕсть еще конфигурационный материал (см. документациюhelp.aliyun.com/document_…), вы можете извлечь часть, чтобы создать элементы конфигурации, такие как «максимальный объем, разрешенный для загрузки». ​

полный код

Ниже приводится использование, инкапсулированное как «сервис».Nest.jsСоответствующий код веб-фреймворка взят из онлайн-проекта автора (слегка изменен и удален) для справки. ​

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

export interface GenerateClientTokenConfig {
  /** 目录名称 */
  dirname: string

  /** 有效时间,单位:小时 */
  expiration?: number

  /** 上传最大体积,单位:MB */
  maxSize?: number
}

/** 直传凭证 */
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()
export class OssService {
  private readonly ossConfig: OssConfig

  constructor() {
    this.ossConfig = {
      bucket: 'xxxxxxxx',
      accessKeyId: 'xxxxxxxx',
      accessKeySecret: 'xxxxxxxx',

      /** OSS 绑定的域名 */
      url: 'xxxxxxxx',
    }
  }

  /**
   * 生成一个可用于客户端直传 OSS 的调用凭证
   *
   * @param config 配置项
   *
   * @see [配置内容](https://help.aliyun.com/document_detail/31988.html#title-6w1-wj7-q4e)
   */
  generateClientToken(config: GenerateClientTokenConfig): ClientToken {
    /** 目录名称 */
    const dirname = config.dirname

    /** 有效时间:默认 4 小时 */
    const timeout = (config.expiration || 4) * 60 * 60 * 1000

    /** 上传最大体积,默认 100M */
    const maxSize = (config.maxSize || 100) * 1024 * 1024

    /** 随机文件名(去掉短横线的 uuid) */
    const filename = uuidv4().replace(/-/gu, '')

    /** 文件路径 */
    const key = dirname + '/' + filename

    /** 到期时间:当前时间 + 有效时间 */
    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],
      ],
    }

    // 将 policyText 转化为 Base64 格式
    const policy = Buffer.from(JSON.stringify(policyText)).toString('base64')

    // 使用 HmacSha1 算法签名
    const signature = crypto.createHmac('sha1', this.ossConfig.accessKeySecret).update(policy, 'utf8').digest('base64')

    return { key, policy, signature, OSSAccessKeyId: accessKeyId, url }
  }
}

После завершения описанного выше метода службы этот метод можно вызвать на уровне «контроллера» для распространения сертификата загрузки, и клиент может напрямую использовать сертификат загрузки для прямой загрузки файла в OSS. ​

2021-08-17.png