#!/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 [--dir folder] [--name filename]') console.log(' node oss-upload.js batch [--dir ]') 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) }) }