UNPKG

n8n-nodes-cos-uploadfile

Version:

A custom n8n node to upload files to Tencent Cloud Object Storage (COS).

274 lines (255 loc) 9.46 kB
import { INodeType, INodeTypeDescription, IExecuteFunctions, INodeExecutionData, NodePropertyTypes, IDataObject, } from 'n8n-workflow'; // 导入腾讯云 COS SDK import COS from 'cos-nodejs-sdk-v5'; // 导入 Node.js 的文件系统模块 (使用 promises API 更方便处理异步) import * as fs from 'fs/promises'; // Changed to fs/promises for async readFile import * as path from 'path'; // Useful for path manipulation export class CosUpload implements INodeType { description: INodeTypeDescription = { displayName: '腾讯云 COS 文件上传', name: 'cosUpload', icon: 'fa:cloud-arrow-up', // 使用一个更贴切的图标 group: ['storage'], // 分组为存储类 version: 1, description: '上传本地文件到腾讯云对象存储 COS', defaults: { name: 'COS 文件上传', }, // 修复 inputs 和 outputs 的类型错误 inputs: ['main'] as any, // 使用 any 绕过严格的类型检查 outputs: ['main'] as any, // 使用 any 绕过严格的类型检查 // 定义节点所需的凭证类型 credentials: [ { name: 'tencentCosApi', // 凭证的内部名称,后续会创建 required: true, }, ], properties: [ // 上传模式选择 { displayName: '上传模式', name: 'uploadMode', type: 'options' as NodePropertyTypes, options: [ { name: '本地文件上传', value: 'localFile' }, { name: 'HTML内容上传', value: 'htmlContent' }, ], default: 'localFile', description: '选择上传模式:本地文件或HTML内容', required: true, }, // 文件路径 { displayName: '本地文件路径', name: 'filePath', type: 'string' as NodePropertyTypes, default: '', placeholder: '/path/to/your/file.txt', description: '要上传的本地文件的绝对路径', required: true, displayOptions: { show: { uploadMode: ['localFile'], }, }, }, // HTML内容 { displayName: 'HTML内容', name: 'htmlContent', type: 'string' as NodePropertyTypes, typeOptions: { rows: 10, }, default: '', placeholder: '<html><body><h1>Hello World</h1></body></html>', description: '要上传的HTML内容', required: true, displayOptions: { show: { uploadMode: ['htmlContent'], }, }, }, // HTML文件名 { displayName: 'HTML文件名', name: 'htmlFileName', type: 'string' as NodePropertyTypes, default: 'index.html', placeholder: 'index.html', description: '生成的HTML文件名', required: true, displayOptions: { show: { uploadMode: ['htmlContent'], }, }, }, // 存储桶名称 { displayName: '存储桶名称 (Bucket)', name: 'bucket', type: 'string' as NodePropertyTypes, default: '', placeholder: 'your-bucket-name-125xxxxxxxx', description: '您的腾讯云 COS 存储桶名称 (包含 APPID)', required: true, }, // 存储桶地域 { displayName: '存储桶地域 (Region)', name: 'region', type: 'options' as NodePropertyTypes, // 使用选项类型 options: [ { name: '北京 (ap-beijing)', value: 'ap-beijing' }, { name: '南京 (ap-nanjing)', value: 'ap-nanjing' }, { name: '上海 (ap-shanghai)', value: 'ap-shanghai' }, { name: '广州 (ap-guangzhou)', value: 'ap-guangzhou' }, { name: '成都 (ap-chengdu)', value: 'ap-chengdu' }, { name: '重庆 (ap-chongqing)', value: 'ap-chongqing' }, { name: '香港 (ap-hongkong)', value: 'ap-hongkong' }, { name: '新加坡 (ap-singapore)', value: 'ap-singapore' }, { name: '硅谷 (na-siliconvalley)', value: 'na-siliconvalley' }, // ... 更多地域可以根据需要添加 ], default: 'ap-guangzhou', description: '存储桶所在的地域', required: true, }, // 存储在桶里的对象键 { displayName: '对象键 (Key)', name: 'key', type: 'string' as NodePropertyTypes, default: '', placeholder: 'images/my-photo.jpg', description: '文件在 COS 中的路径和名称', required: true, }, ], }; async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> { const items = this.getInputData(); // 获取输入数据 (如果需要从上一个节点获取文件路径等) const returnData: INodeExecutionData[] = []; // 获取凭证 const credentials = (await this.getCredentials('tencentCosApi')) as unknown as IDataObject; const secretId = credentials.secretId as string; const secretKey = credentials.secretKey as string; // 获取节点参数 const uploadMode = this.getNodeParameter('uploadMode', 0) as string; const bucket = this.getNodeParameter('bucket', 0) as string; const region = this.getNodeParameter('region', 0) as string; const key = this.getNodeParameter('key', 0) as string; // 根据上传模式获取相应参数 let filePath = ''; let htmlContent = ''; let htmlFileName = ''; if (uploadMode === 'localFile') { filePath = this.getNodeParameter('filePath', 0) as string; } else if (uploadMode === 'htmlContent') { htmlContent = this.getNodeParameter('htmlContent', 0) as string; htmlFileName = this.getNodeParameter('htmlFileName', 0) as string; } // 确保 SecretId 和 SecretKey 存在 if (!secretId || !secretKey) { throw new Error('腾讯云 COS 凭证 (SecretId 和 SecretKey) 未配置或不完整。'); } // 初始化 COS 实例 const cos = new COS({ SecretId: secretId, SecretKey: secretKey, }); for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { try { if (uploadMode === 'localFile') { // 本地文件上传 // 执行文件上传 const uploadResult = await new Promise<COS.PutObjectResult>((resolve, reject) => { cos.uploadFile( { Bucket: bucket, Region: region, Key: key, FilePath: filePath, // 本地文件路径 }, function (err, data) { if (err) { return reject(err); } resolve(data); }, ); }); // 将上传结果添加到输出数据 returnData.push({ json: { success: true, message: `文件 '${filePath}' 成功上传到 COS`, cosResponse: uploadResult as unknown as IDataObject, }, pairedItem: { item: itemIndex, }, }); } else if (uploadMode === 'htmlContent') { // HTML内容上传 const htmlFilePath = path.join(process.cwd(), htmlFileName); // 先写入HTML内容到临时文件 await fs.writeFile(htmlFilePath, htmlContent); // 执行文件上传 const uploadResult = await new Promise<COS.PutObjectResult>((resolve, reject) => { cos.uploadFile( { Bucket: bucket, Region: region, Key: key, FilePath: htmlFilePath, // 使用刚创建的临时文件路径 }, function (err, data) { if (err) { return reject(err); } resolve(data); }, ); }); // 将上传结果添加到输出数据 returnData.push({ json: { success: true, message: `HTML内容成功上传到 COS`, cosResponse: uploadResult as unknown as IDataObject, }, pairedItem: { item: itemIndex, }, }); // 清理临时文件 await fs.unlink(htmlFilePath); } } catch (error: unknown) { const errorMessage = (error instanceof Error) ? error.message : String(error); const files = await fs.readdir('/home/node/.cache/n8n/public'); returnData.push({ json: { success: false, message: `文件上传失败 ${JSON.stringify(files)}`, error: errorMessage, }, pairedItem: { item: itemIndex, }, }); } } return this.prepareOutputData(returnData); } }