172 lines
5.2 KiB
JavaScript
172 lines
5.2 KiB
JavaScript
|
|
#!/usr/bin/env node
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* OSS 文件上传工具
|
|||
|
|
*
|
|||
|
|
* 上传图片/视频到阿里云 OSS,返回签名 URL。
|
|||
|
|
* 支持单文件和批量上传。
|
|||
|
|
*
|
|||
|
|
* 用法:
|
|||
|
|
* node oss-upload.js ./image.png
|
|||
|
|
* node oss-upload.js ./video.mp4 --dir videos/
|
|||
|
|
* node oss-upload.js batch ./manifest.json
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
const OSS = require('ali-oss')
|
|||
|
|
const path = require('path')
|
|||
|
|
const fs = require('fs')
|
|||
|
|
|
|||
|
|
// ============================================================================
|
|||
|
|
// 配置
|
|||
|
|
// ============================================================================
|
|||
|
|
|
|||
|
|
function getConfig() {
|
|||
|
|
const configPath = path.join(__dirname, '..', '..', 'config.json')
|
|||
|
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'))
|
|||
|
|
if (!config.ossRegion || !config.ossAccessKeyId || !config.ossAccessKeySecret || !config.ossBucket) {
|
|||
|
|
console.error('config.json 需要填写 ossRegion, ossAccessKeyId, ossAccessKeySecret, ossBucket')
|
|||
|
|
process.exit(1)
|
|||
|
|
}
|
|||
|
|
return config
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function createClient(config) {
|
|||
|
|
return new OSS({
|
|||
|
|
region: config.ossRegion,
|
|||
|
|
accessKeyId: config.ossAccessKeyId,
|
|||
|
|
accessKeySecret: config.ossAccessKeySecret,
|
|||
|
|
bucket: config.ossBucket,
|
|||
|
|
secure: true,
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ============================================================================
|
|||
|
|
// 上传
|
|||
|
|
// ============================================================================
|
|||
|
|
|
|||
|
|
async function uploadFile(filePath, options = {}) {
|
|||
|
|
const config = getConfig()
|
|||
|
|
const client = createClient(config)
|
|||
|
|
|
|||
|
|
if (!fs.existsSync(filePath)) {
|
|||
|
|
throw new Error(`文件不存在: ${filePath}`)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const folder = options.folder || config.ossFolder || 'tmp/'
|
|||
|
|
const basename = options.name || path.basename(filePath)
|
|||
|
|
const ossPath = `${folder}${basename}`
|
|||
|
|
|
|||
|
|
const buffer = fs.readFileSync(filePath)
|
|||
|
|
await client.put(ossPath, buffer)
|
|||
|
|
|
|||
|
|
const expires = config.ossExpires || 31536000
|
|||
|
|
const url = client.signatureUrl(ossPath, { expires })
|
|||
|
|
|
|||
|
|
return { url, ossPath, size: buffer.length }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function uploadBuffer(buffer, options = {}) {
|
|||
|
|
const config = getConfig()
|
|||
|
|
const client = createClient(config)
|
|||
|
|
|
|||
|
|
const folder = options.folder || config.ossFolder || 'tmp/'
|
|||
|
|
const basename = options.name || `${Date.now()}${options.ext || '.png'}`
|
|||
|
|
const ossPath = `${folder}${basename}`
|
|||
|
|
|
|||
|
|
await client.put(ossPath, buffer)
|
|||
|
|
|
|||
|
|
const expires = config.ossExpires || 31536000
|
|||
|
|
const url = client.signatureUrl(ossPath, { expires })
|
|||
|
|
|
|||
|
|
return { url, ossPath }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ============================================================================
|
|||
|
|
// 批量上传(读 manifest.json 中的 file 列表)
|
|||
|
|
// ============================================================================
|
|||
|
|
|
|||
|
|
async function batchUpload(manifestPath, baseDir) {
|
|||
|
|
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'))
|
|||
|
|
const dir = baseDir || path.dirname(manifestPath)
|
|||
|
|
const results = {}
|
|||
|
|
|
|||
|
|
for (const item of manifest.items) {
|
|||
|
|
const filePath = path.join(dir, item.file)
|
|||
|
|
if (!fs.existsSync(filePath)) continue
|
|||
|
|
|
|||
|
|
const name = path.basename(item.file)
|
|||
|
|
try {
|
|||
|
|
const { url } = await uploadFile(filePath, { name })
|
|||
|
|
results[item.file] = url
|
|||
|
|
console.log(` OK: ${name}`)
|
|||
|
|
} catch (err) {
|
|||
|
|
console.error(` FAIL: ${name} - ${err.message}`)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return results
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ============================================================================
|
|||
|
|
// CLI
|
|||
|
|
// ============================================================================
|
|||
|
|
|
|||
|
|
function parseArgs(argv) {
|
|||
|
|
const args = { _: [] }
|
|||
|
|
for (let i = 0; i < argv.length; i++) {
|
|||
|
|
if (argv[i].startsWith('--')) {
|
|||
|
|
const key = argv[i].slice(2)
|
|||
|
|
const val = argv[i + 1]
|
|||
|
|
if (val && !val.startsWith('--')) { args[key] = val; i++ }
|
|||
|
|
else args[key] = true
|
|||
|
|
} else {
|
|||
|
|
args._.push(argv[i])
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return args
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function main() {
|
|||
|
|
const args = parseArgs(process.argv.slice(2))
|
|||
|
|
const cmd = args._[0]
|
|||
|
|
|
|||
|
|
if (!cmd) {
|
|||
|
|
console.log('用法: node oss-upload.js <file> [--dir folder] [--name filename]')
|
|||
|
|
console.log(' node oss-upload.js batch <manifest.json> [--dir <baseDir>]')
|
|||
|
|
process.exit(0)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (cmd === 'batch') {
|
|||
|
|
const manifest = args._[1]
|
|||
|
|
if (!manifest) { console.error('指定 manifest.json'); process.exit(1) }
|
|||
|
|
console.log(`批量上传: ${manifest}`)
|
|||
|
|
const results = await batchUpload(manifest, args.dir)
|
|||
|
|
console.log(`\n完成: ${Object.keys(results).length} 个文件`)
|
|||
|
|
// 写回 urls
|
|||
|
|
const urlsPath = path.join(args.dir || path.dirname(manifest), 'urls.json')
|
|||
|
|
const existing = fs.existsSync(urlsPath) ? JSON.parse(fs.readFileSync(urlsPath, 'utf-8')) : {}
|
|||
|
|
Object.assign(existing, results)
|
|||
|
|
fs.writeFileSync(urlsPath, JSON.stringify(existing, null, 2))
|
|||
|
|
console.log(`URLs 已写入: ${urlsPath}`)
|
|||
|
|
} else {
|
|||
|
|
const filePath = path.resolve(cmd)
|
|||
|
|
console.log(`上传: ${filePath}`)
|
|||
|
|
const { url, ossPath, size } = await uploadFile(filePath, {
|
|||
|
|
folder: args.dir,
|
|||
|
|
name: args.name,
|
|||
|
|
})
|
|||
|
|
console.log(`\nOSS 路径: ${ossPath}`)
|
|||
|
|
console.log(`签名 URL: ${url}`)
|
|||
|
|
console.log(`文件大小: ${(size / 1024).toFixed(1)} KB`)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
module.exports = { uploadFile, uploadBuffer, batchUpload }
|
|||
|
|
|
|||
|
|
if (require.main === module) {
|
|||
|
|
main().catch(err => {
|
|||
|
|
console.error(`错误: ${err.message}`)
|
|||
|
|
process.exit(1)
|
|||
|
|
})
|
|||
|
|
}
|