完成常用方法

master
yutent 2023-02-17 19:25:30 +08:00
parent 1ddf3df083
commit 6f9c0b0f4a
3 changed files with 281 additions and 0 deletions

View File

@ -0,0 +1,174 @@
/**
* 阿里云oss, 自签名
* @author yutent<yutent@doui.cc>
* @date 2020/01/18 14:28:47
*/
import { hmac, base64encode } from 'crypto'
import xml2js from './lib/xml2js.js'
import {
APP_ID,
APP_KEY,
MIME_TYPES,
DEFAULT_MIME_TYPE
} from './lib/constants.js'
import { getMimeType, fixFile, str2sign } from './lib/helper.js'
export default class Alioss {
#bucket = ''
#domain = ''
#api = ''
constructor(bucket, domain, region) {
this.#bucket = bucket
this.#domain = domain
this.#api = `https://${bucket}.${region}.aliyuncs.com`
}
// 授权签名, 用于临时下载私有bucket的文件
auth(key) {
var time = Math.floor(Date.now() / 1000) + 1800 // 半小时内
return hmac(
'SHA-1',
`GET\n\n\n${time}\n/${this.#bucket}/${key}`,
APP_KEY,
'base64'
).then(signature => {
return `?OSSAccessKeyId=${APP_ID}&Expires=${time}&Signature=${encodeURIComponent(
signature
)}`
})
}
/**
* {生成签名, 需传入 , 大小限制}
* dir: 上传目录名
* size: 大小限制, 单位 MB 默认10MB
*/
sign(dir = '', size = 10) {
var time = new Date()
var params = {
conditions: [
['content-length-range', 0, Math.floor(1024 * 1024 * size)],
['starts-with', '$key', dir ? dir.replace(/\/+$/, '') + '/' : '']
]
}
var policy = ''
time.setTime(time.getTime() + 60 * 60 * 1000) // 60分钟内有效
params.expiration = time.toISOString()
policy = JSON.stringify(params)
policy = btoa(policy)
return hmac('SHA-1', policy, APP_KEY, 'base64').then(signature => {
return { policy, signature, id: APP_ID }
})
}
list({ prefix = '', delimiter = '/', max = 1000, token } = {}) {
var time = new Date().toGMTString()
var query = {
'list-type': 2,
prefix,
delimiter,
'max-keys': max,
'continuation-token': token
}
return hmac(
'SHA-1',
`GET\n\n\n${time}\nx-oss-date:${time}\n/${this.#bucket}/${
token ? '?continuation-token=' + token : ''
}`,
APP_KEY,
'base64'
)
.then(signature =>
fetch(this.#api + '?' + query.toParams(), {
headers: {
'content-type': void 0,
authorization: `OSS ${APP_ID}:${signature}`,
'x-oss-date': time
}
})
)
.then(r => r.text())
.then(r => xml2js(r))
}
/**
* {上传文件}
* auth: 上面的sign签名结果
* file: 要上传的文件 <Blob> | <File>
* key: 要保存的文件名, 带完整路径
*/
upload(auth, file, key) {
var body = new FormData()
if (!file.type) {
let ext = file.name.split('.').pop()
if (ext && MIME_TYPES[ext]) {
file = fixFile(ext, file)
}
}
body.append('key', key) //
body.append('policy', auth.policy)
body.append('OSSAccessKeyId', auth.id)
body.append('Signature', auth.signature)
body.append('file', file)
return fetch(this.#api, { method: 'POST', body }).then(
r => this.#domain + key
)
}
copy(origin, target) {
var time = new Date().toGMTString()
var headers = {
'x-oss-date': time,
'x-oss-copy-source': `/${this.#bucket}/${encodeURIComponent(origin)}`
}
return hmac(
'SHA-1',
str2sign('PUT', this.#bucket, {
time,
headers,
key: target
}),
APP_KEY,
'base64'
)
.then(signature =>
fetch(`${this.#api}/${target}`, {
method: 'PUT',
headers: {
authorization: `OSS ${APP_ID}:${signature}`,
...headers
}
})
)
.catch(e => console.log(e))
}
delete(key) {
var time = new Date().toGMTString()
return hmac(
'SHA-1',
`DELETE\n\n\n${time}\nx-oss-date:${time}\n/${this.#bucket}/${key}`,
APP_KEY,
'base64'
).then(signature =>
fetch(`${this.#api}/${key}`, {
method: 'DELETE',
headers: {
'content-type': void 0,
authorization: `OSS ${APP_ID}:${signature}`,
'x-oss-date': time
}
})
)
}
}

41
src/lib/constants.js Normal file
View File

@ -0,0 +1,41 @@
/**
* {}
* @author yutent<yutent.io@gmail.com>
* @date 2023/02/17 16:00:06
*/
export const DEFAULT_MIME_TYPE = 'application/octet-stream'
export const MIME_TYPES = {
html: 'text/html',
json: 'application/json',
js: 'application/javascript',
htm: 'text/html',
txt: 'text/plain',
css: 'text/css',
webp: 'image/webp',
jpg: 'image/jpg',
jpeg: 'image/jpeg',
png: 'image/png',
gif: 'image/gif',
svg: 'image/svg+xml',
ico: 'image/ico',
mp3: 'audio/mpeg',
ogg: 'audio/ogg',
m4a: 'audio/m4a',
amr: 'audio/amr',
mp4: 'video/mp4',
webm: 'video/webm',
wasm: 'application/wasm',
zip: 'application/zip',
'7z': 'application/x-7z-compressed',
eot: 'application/vnd.ms-fontobject',
ttf: 'font/ttf',
otf: 'font/otf',
woff: 'font/woff',
woff2: 'font/woff2',
xls: 'application/vnd.ms-excel',
doc: 'application/msword',
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
}

66
src/lib/helper.js Normal file
View File

@ -0,0 +1,66 @@
/**
* {}
* @author yutent<yutent.io@gmail.com>
* @date 2023/02/17 19:14:08
*/
import { MIME_TYPES, DEFAULT_MIME_TYPE } from './constants.js'
// 把文件大小, 转为友好的格式
export function parseSize(num) {
if (num < 1024) {
return `${num} B`
} else {
num = (num / 1024).toFixed(2) - 0
if (num < 1024) {
return `${num} KB`
} else {
num = (num / 1024).toFixed(2) - 0
return `${num} MB`
}
}
}
// 获取文件的拓展名
export function getExt(str = '') {
let tmp = str.split('.')
let ext
if (tmp.length > 1) {
ext = tmp.pop()
if (ext === 'xz' || ext === 'gz') {
ext = tmp.pop() + ext
}
return ext
}
return 'unknow'
}
export function getMimeType(name) {
var ext = getExt(name)
return MIME_TYPES[ext] || DEFAULT_MIME_TYPE
}
export function fixFile(name, data) {
return new File([data], name, { type: getMimeType(name) })
}
// 生成要签名的字符串
export function str2sign(
method = 'GET',
bucket,
{ time, headers = {}, key, query } = {}
) {
let arr = [
method,
'', // 请求内容的md5值, 用于服务端校验文件是否完整. 可以为空
'', // 请求文件的content-type类型, 可以为空
time,
Object.keys(headers)
.sort()
.map(k => `${k}:${headers[k]}`)
.join('\n'),
`/${bucket}/${key + (query || '')}`
]
return arr.join('\n')
}