阿里云OSS實(shí)踐文件直傳基于服務(wù)端
前言
在日常開發(fā)中,客戶端上傳文件的一般流程是:客戶端向服務(wù)端發(fā)送文件,再由服務(wù)端將文件轉(zhuǎn)儲(chǔ)到專門的存儲(chǔ)服務(wù)器或云計(jì)算廠商的儲(chǔ)存服務(wù)(例如阿里云 OSS)上,這樣做的一個(gè)弊端是上傳環(huán)節(jié)占用服務(wù)器的帶寬,個(gè)位數(shù)的并發(fā)上傳就能把帶寬占滿,從而導(dǎo)致用戶體驗(yàn)下降。而如果直接將文件從客戶端直傳到第三方的存儲(chǔ)服務(wù)上,就可以避免這個(gè)問題。
本文以阿里云 OSS(Object Storage Service,對(duì)象存儲(chǔ)服務(wù))為例,詳細(xì)說明將文件從客戶端直傳 OSS 的整體流程,并提供了完整的代碼演示。
優(yōu)缺點(diǎn)
從“客戶端 — 服務(wù)器 — OSS”的傳輸模式,變更為“客戶端 — OSS”的模式,最大的好處是,省掉了上傳服務(wù)器的這一步,上傳效率更高,速度更快(相比較于一般服務(wù)器的帶寬,可以認(rèn)為 OSS 的寬帶是“幾乎無限”的)。
當(dāng)然該模式也有缺點(diǎn),那就是增加了很多額外的開發(fā)工作量,主要包含 2 部分:
(1)服務(wù)端增加生成上傳 OSS 憑證的代碼。
(2)客戶端增加從服務(wù)端獲取上傳 OSS 憑證的代碼和對(duì)直傳 OSS 進(jìn)行適配。
整體而言,直傳模式除了增加一點(diǎn)開發(fā)工作量以外,從架構(gòu)層面,幾乎沒有任何缺點(diǎn)。
流程
實(shí)際上,整個(gè)流程非常簡單,包含了兩步:
(1)客戶端向服務(wù)端發(fā)送請(qǐng)求,獲取直傳 OSS 的憑證。
(2)客戶端向 OSS 上傳文件,并攜帶該憑證。
邏輯拆解
關(guān)于如何生成憑證(也叫簽名),可以閱讀官方文檔(help.aliyun.com/document_de…),但由于文檔創(chuàng)建時(shí)間比較早,對(duì)于新手很難看懂,本文將手把手給你演示整個(gè)過程。 ?
整個(gè)“生成上傳 OSS 憑證”過程,實(shí)際上做了這么幾件事:
(1)上傳憑證鑒權(quán)由 policy
提供,根據(jù)私密配置生成這個(gè) policy
。
(2)由于上傳環(huán)節(jié)脫離了開發(fā)者服務(wù)器,因此你可以在 policy
中定義各種限制,例如上傳最大體積、文件名等。
(3)將 policy
轉(zhuǎn)化為指定的格式。
代碼實(shí)現(xiàn)
我們先考慮將流程的每一步實(shí)現(xiàn),然后再將流程代碼封裝成函數(shù)。
OSS 配置
首先定義 OSS 的配置文件,關(guān)于配置項(xiàng)的內(nèi)容,可以參考文檔:help.aliyun.com/document_de…
/** OSS 配置項(xiàng) */ const ossConfig = { bucket: 'xxxxxxxx', accessKeyId: 'xxxxxxxx', accessKeySecret: 'xxxxxxxx', /** OSS 綁定的域名 */ url: 'xxxxxxxx', }
policy 內(nèi)容
對(duì)于 policy
,有很多配置項(xiàng),我們先考慮生成“寫死”的模式,然后再優(yōu)化為由函數(shù)參數(shù)傳入配置項(xiàng)。以下是一個(gè)最基礎(chǔ)的 policy
。 ?
有效期
首先定義一個(gè)有效時(shí)長(單位:毫秒),然后該憑證的有效截止時(shí)間則為“當(dāng)前時(shí)間 + 有效時(shí)長”,最后需要轉(zhuǎn)化為 ISO 時(shí)間字符串格式。 ?
/** 有效時(shí)長:例如 4 小時(shí) */ const timeout = 4 * 60 * 60 * 1000 /** 到期時(shí)間:當(dāng)前時(shí)間 + 有效時(shí)間 */ const expiration = new Date(Date.now() + timeout).toISOString()
文件名
文件名建議使用 UUID(筆者習(xí)慣性使用去掉短橫線的 UUID),避免重復(fù)。 ?
import { v4 as uuidv4 } from 'uuid' /** 隨機(jī)文件名(去掉短橫線的 uuid) */ const filename = uuidv4().replace(/-/gu, '')
一般建議按照不同的業(yè)務(wù)模塊,將文件劃分不同的目錄,例如這里使用 file
目錄,那么完整的 OSS 文件路徑則為: ?
/** 目錄名稱 */ const dirname = 'file' /** 文件路徑 */ const key = dirname + '/' + filename
需要注意的是,文件路徑不能以 “/” 開頭(OSS 本身的要求)。 ?
將以上內(nèi)容整合,就形成了 policy
文本,以下是一個(gè)基礎(chǔ)格式: ?
const policyText = { expiration: expiration, conditions: [ ['eq', '$bucket', ossConfig.bucket], ['eq', '$key', key], ], }
轉(zhuǎn)化 policy
將 policyText
轉(zhuǎn)化為 Base64
格式后,就是要求的 policy
了。
// 將 policyText 轉(zhuǎn)化為 Base64 格式 const policy = Buffer.from(JSON.stringify(policyText)).toString('base64')
然后對(duì) policy
使用 OSS 密鑰使用 HmacSha1 算法簽名簽名。
import * as crypto from 'crypto' // 使用 HmacSha1 算法簽名 const signature = crypto.createHmac('sha1', ossConfig.accessKeySecret).update(policy, 'utf8').digest('base64')
最后將上述流程中的相關(guān)字段返回給客戶端,即為“上傳憑證”。 ?
進(jìn)一步分析
以上完整演示了整個(gè)流程,我們進(jìn)一步分析,如何將其封裝為一個(gè)通用性的函數(shù)。 ?
(1)憑證的有效時(shí)長可以根據(jù)不同的業(yè)務(wù)模塊分別定義,于是做成函數(shù)配置項(xiàng)。
(2)目錄名稱也可以做成配置項(xiàng)。
(3) policy
還有更多的配置內(nèi)容(見文檔 help.aliyun.com/document_de…),可以抽取一部分做成配置項(xiàng),例如“允許上傳的最大體積”。
完整代碼
以下是封裝為“服務(wù)”的使用 Nest.js
Web 框架的相關(guān)代碼,來源自筆者的線上項(xiàng)目(略有調(diào)整和刪改),供參考。
import { Injectable } from '@nestjs/common' import * as crypto from 'crypto' import { v4 as uuidv4 } from 'uuid' export interface GenerateClientTokenConfig { /** 目錄名稱 */ dirname: string /** 有效時(shí)間,單位:小時(shí) */ 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', } } /** * 生成一個(gè)可用于客戶端直傳 OSS 的調(diào)用憑證 * * @param config 配置項(xiàng) * * @see [配置內(nèi)容](https://help.aliyun.com/document_detail/31988.html#title-6w1-wj7-q4e) */ generateClientToken(config: GenerateClientTokenConfig): ClientToken { /** 目錄名稱 */ const dirname = config.dirname /** 有效時(shí)間:默認(rèn) 4 小時(shí) */ const timeout = (config.expiration || 4) * 60 * 60 * 1000 /** 上傳最大體積,默認(rèn) 100M */ const maxSize = (config.maxSize || 100) * 1024 * 1024 /** 隨機(jī)文件名(去掉短橫線的 uuid) */ const filename = uuidv4().replace(/-/gu, '') /** 文件路徑 */ const key = dirname + '/' + filename /** 到期時(shí)間:當(dāng)前時(shí)間 + 有效時(shí)間 */ 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 轉(zhuǎn)化為 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 } } }
在完整以上服務(wù)方法后,后續(xù)就可以在“控制器”層調(diào)用該方法用于分發(fā)上傳憑證,客戶端可直接使用該上傳憑證將文件直傳至 OSS 中。 ?
以上就是阿里云OSS實(shí)踐文件直傳基于服務(wù)端的詳細(xì)內(nèi)容,更多關(guān)于OSS文件直傳服務(wù)端的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
利用Node.js制作爬取大眾點(diǎn)評(píng)的爬蟲
相信每位用過大眾點(diǎn)評(píng)的人都知道,大眾點(diǎn)評(píng)上有很多美食餐館的信息,所以這篇文章給大家分享利用Node.js實(shí)現(xiàn)爬取大眾點(diǎn)評(píng)的爬蟲,正好可以拿來練練手Node.js。感興趣的可以參考借鑒。2016-09-09Node.js + Redis Sorted Set實(shí)現(xiàn)任務(wù)隊(duì)列
本文給大家分享的是使用Node.js + Redis Sorted Set實(shí)現(xiàn)任務(wù)隊(duì)列的方法示例,非常的實(shí)用,有需要的小伙伴可以參考下2016-09-09node簡單實(shí)現(xiàn)一個(gè)更改頭像功能的示例
本篇文章主要介紹了node簡單實(shí)現(xiàn)一個(gè)更改頭像功能的示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-12-12