Files
video-create/.claude/skills/video-from-script/scripts/oss-upload.js
lc 9cbdabda31 feat: 封面生成流水线、执黑先行二号风格扩展、账号配置更新
- 新增 gen-covers 系列脚本(kling/gpt/t2i/batch/direct/final等方案)
- 执黑先行二号添加9种风格提示词目录(梦核/剪纸/水墨/毛毡/硬核线条等)
- 执黑先行添加封面提示词、执黑先行二号更新图片提示词
- product_viral_factory 账号配置扩充并添加 cover_template 参考图
- capcut_assemble/kling-video-generator/oss-upload/poll-utils 细节修复
- CLAUDE.md 更新流程文档

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 16:44:47 +08:00

175 lines
5.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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 stat = fs.statSync(filePath)
const timeout = stat.size > 50 * 1024 * 1024 ? 600000 : 300000
const stream = fs.createReadStream(filePath)
await client.putStream(ossPath, stream, { timeout, contentLength: stat.size })
const expires = config.ossExpires || 31536000
const url = client.signatureUrl(ossPath, { expires })
return { url, ossPath, size: stat.size }
}
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)
})
}