предисловие
В повседневной разработке общий процесс загрузки файлов с клиента выглядит следующим образом: клиент отправляет файл на сервер, а сервер выгружает файл на выделенный сервер хранения или в службу хранения поставщика облачных вычислений (например, 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.