@es-labs/node
Version:
Reusable library
280 lines (252 loc) • 8.74 kB
JavaScript
// Aliyun OSS interface - https://github.com/ali-sdk/ali-oss
// suitable for files that are not large... limit to 10Mb file size
//
// res.status = 204 no content but still success
// res.status = 200
// res.statusMessage = 200
// TDB signatureUrlV4 & usage
import 'dotenv/config.js' // node --env-file .env
import OSS from 'ali-oss'
const { OSS_AK_ID, OSS_AK_SECRET, OSS_REGION, OSS_BUCKET } = process.env
const store = (OSS_AK_ID && OSS_AK_SECRET && OSS_REGION) ? new OSS({
region: OSS_REGION,
accessKeyId: OSS_AK_ID,
accessKeySecret: OSS_AK_SECRET,
bucket: OSS_BUCKET,
}) : null
/**
* get count of objects in a bucket.
* @param {{ bucketName: string }}
* @returns {{ status: Number, count: Number }}
*/
const countBucketObjects = async (bucketName = null) => {
try {
const result = await store.getBucketStat(bucketName)
return { status: 200, count: result?.stat?.ObjectCount }
} catch (e) {
// status 404, code: NoSuchBucket
return { status: e.status, count: 0 }
}
}
/**
* A point on a two dimensional plane.
* @typedef {Object} ListV2Object
* @property {string} name - e.g. arco/a5.wav
* @property {string} url - e.g. http://my-bucket.oss-ap-southeast-1.aliyuncs.com/test/a5.wav
* @property {string} lastModified - e.g. 2024-09-03T02:34:24.000Z
* @property {string} etag - e.g. "3A627F876FD033F4B5CB81F063F9F883"
* @property {string} type - e.g. Normal
* @property {Number} size - e.g. 948328
* @property {string} storageClass - e.g. Standard
* @property {string} owner - e.g. null
*/
/**
* get count of objects in a bucket.
* @param {{ prefix: string, maxKeys: Number }}
* @returns {{ status: Number, statusMessage: string, [objects]: ListV2Object[] }}
*/
const listObjects = async ({ prefix = '', maxKeys = 10 } = {}) => {
// console.log(prefix, maxKeys)
try {
const result = await store.listV2({
prefix,
'max-keys': maxKeys
})
// console.log(result.res)
const { status, statusMessage } = result.res
return {
status,
statusMessage,
objects: result.objects
}
} catch (e) {
console.log('ali - listObjects', e)
return { status: 500, statusMessage: e.toString() }
}
}
/**
* put object in a bucket.
* @param {string} key - the object key - e.g. test/hello.txt
* @param {string|Buffer|ReadableStream} payload - file data
* @returns {{ status: Number, statusMessage: string }}
*/
const putObject = async (key, payload) => {
// if (!store) return null
try {
const result = await store.put(key, payload) //
// console.log(result)
const { status, statusMessage } = result.res
return { status, statusMessage }
} catch (e) {
console.log('ali - putObject', e)
return { status: 500, statusMessage: e.toString() }
}
}
/**
* get object.
* @param {string} key - the object key - e.g. test/hello.txt
* @param {string|Buffer|ReadableStream} payload - file data
* @returns {{ status: Number, statusMessage: string, [buffer]: Buffer }}
*/
const getObject = async (key) => {
// if (!store) return null
try {
const result = await store.get(key)
// console.log(typeof result.content, result.content.toString('utf-8')) // content is Buffer object
// console.log(Buffer.isBuffer(result.content))
// console.log(result)
const { status, statusMessage} = result?.res || {}
return {
status,
statusMessage,
buffer: result?.content
}
} catch (e) {
console.log('ali - getObject', e)
return { status: 500, statusMessage: e.toString() }
}
}
/**
* get object.
* @param {string[]} keys - the object keys - e.g. ['test/hello.txt','abc/d123.txt']
* @returns {{ status: Number, statusMessage: string, [deleted]: { Key: string }[] }}
*/
const deleteObjects = async (keys) => {
// if (!store) return null
try {
const result = await store.deleteMulti(keys, {})
// console.log(result)
const { status, statusMessage } = result?.res || {}
return {
status,
statusMessage,
deleted: result.deleted
}
} catch (e) {
console.log('ali - deleteObjects', e)
return { status: 500, statusMessage: e.toString() }
}
}
// (method, expires[, request, objectName, additionalHeaders])
/**
* get signed URL.
* @param {string} method - GET or PUT
* @param {number} expires - expiration in seconds
* @param {string} key - the object key - e.g. test/hello.txt
*/
const getSignedUrl = async (method, expires, key, headers = null, additional = null) => {
const signedUrl = await store.signatureUrlV4(method, expires, headers, key, additional)
console.log(signedUrl)
return signedUrl
}
/**
* get a signed URL to write a file to OSS.
* @param {string} directory - the directory in OSS.
* @param {string} filename - the filename
* @param {string} contentType - the content type of the file
* @param {string} [action='write'] - 'write' or 'read'
* @param {number} [expiration=7200] - the expiration time of the signed URL
* @param {object} [callbackConfig] - the callback config for write action, example: { callback_url: 'https://example.com', body: { foo: 'bar' } }
* @returns {{ url: string, error?: string }}
*/
const getUploadURL = async (directory, filename, contentType, action = 'write', expiration = 7200, callbackConfig) => {
if (!action || !filename) {
return { error: 'filename and action required' }
}
try {
let url
// write / new file action
if (action === 'write') {
let arr = filename.split('.')
arr[0] = crypto
.createHash('sha256')
.update(arr[0] + new Date().getTime())
.digest('hex')
let newFilename = arr.join('.')
const fullPath = directory ? `${directory}/${newFilename}` : newFilename
url = await store.signatureUrl(fullPath, {
expires: expiration,
method: 'PUT',
'Content-Type': contentType,
callback: {
url: callbackConfig?.callback_url,
body: JSON.stringify({...callbackConfig?.body, filename, filepath: fullPath}),
contentType: 'application/json'
}
})
} else {
url = await store.signatureUrl(filename)
}
return { url }
} catch (e) {
return { error: e.toString() }
}
}
const test = async () => {
// [bucket count]
// const bucketObjCount = await countBucketObjects('no-such-bucket') // non-existing bucket, also test with existing bucket
// console.log('bucketObjCount', bucketObjCount) // -1 if error ?
// [put] - if put same object name will replace...
// const testFile1 = new File(['Hello, world 1!'], 'hello.txt', { type: 'text/plain' })
// const testData1 = await testFile1.arrayBuffer()
// const putRes1 = await putObject('hello1.txt', Buffer.from(testData1))
// console.log('putRes1', putRes1)
// const testFile2 = new File(['Hello, world 2!'], 'hello.txt', { type: 'text/plain' })
// const testData2 = await testFile2.arrayBuffer()
// const putRes2 = await putObject('hello2.txt', Buffer.from(testData2))
// console.log('putRes2', putRes2)
// [list objects]
// const listRes = await listObjects({ prefix: 'hello' })
// console.log('listRes.objects', listRes?.objects?.length, listRes?.objects?.map(item => item.name))
// [get object]
// const data1 = await getObject('hello1.txt')
// console.log('data1', data1?.buffer?.toString())
// const data2 = await getObject('hello2.txt')
// console.log('data2', data2?.buffer?.toString())
// [delete objects]
// const deleteRes = await deleteObjects(['ahello1.txt', 'ahello2.txt', 'ahello3.txt'])
// console.log(deleteRes)
const url = await getSignedUrl('GET', 60, 'hello1.txt')
/*
// -------------------------------------------------
// PutObject
const putObejctUrl = await store.signatureUrlV4('PUT', 60, undefined, 'your obejct name');
console.log(putObejctUrl);
// --------------------------------------------------
const putObejctUrl = await store.signatureUrlV4(
'PUT',
60,
{
headers: {
'Content-Type': 'text/plain',
'Content-MD5': 'xxx',
'Content-Length': 1
}
},
'your obejct name',
['Content-Length']
);
console.log(putObejctUrl);
*/
}
// test()
// async function listBucketInventory() {
// const bucket = 'Your Bucket Name'
// let nextContinuationToken
// // list all inventory of the bucket
// do {
// const result = await store.listBucketInventory(bucket, nextContinuationToken)
// console.log(result.inventoryList)
// nextContinuationToken = result.nextContinuationToken
// } while (nextContinuationToken)
// }
// listBucketInventory()
export {
countBucketObjects,
listObjects,
putObject,
getObject,
deleteObjects,
getSignedUrl,
getUploadURL
}