Camunda Node.js外部任务OpenKM文件链接存流程变量为空问题
问题根因
变量值为空和encodeURIComponent没有任何关系,核心原因是Node.js异步执行时序错误:
exec是异步非阻塞方法,调用后不会等待curl请求执行完成、拿到OpenKM返回结果,代码会直接向下执行- 你执行
processVariables.set("link", newlink)和taskService.complete时,exec的回调逻辑还没触发,newlink还是初始定义的空字符串,最终存入流程的变量值自然为空 - 另外你代码里下载Camunda附件的逻辑也存在相同时序问题:发起
http.get下载请求后,没等文件完全写入磁盘就执行上传,大概率会出现上传0字节损坏文件的问题。
修复方案
用Promise包装所有异步操作,配合async/await保证执行顺序,等上一步操作拿到结果后再走后续流程,修复后完整代码如下:
const { Client, logger } = require("camunda-external-task-client-js"); const { Variables } = require("camunda-external-task-client-js"); const { exec } = require('child_process'); const http = require('http'); const fs = require('fs'); // 未使用moment可删除该行及对应依赖 var moment = require("moment"); const config = { baseUrl: "http://localhost:8080/engine-rest" }; const client = new Client(config); // 包装文件下载逻辑,等待文件写入完成再继续 function downloadFile(url, savePath) { return new Promise((resolve, reject) => { const file = fs.createWriteStream(savePath); http.get(url, (response) => { response.pipe(file); file.on("finish", () => { file.close(); resolve(); }); file.on("error", (err) => { fs.unlink(savePath, () => {}); reject(err); }) }).on("error", reject); }) } // 包装OpenKM上传逻辑,等待接口返回后生成访问链接 function uploadToOpenKM(cmd) { return new Promise((resolve, reject) => { exec(cmd, (err, stdout, stderr) => { if (err) { console.log("OpenKM上传错误: " + stderr); reject(err); return; } const res = JSON.parse(stdout); const accessLink = `http://172.20.16.90:8080/OpenKM/frontend/index.jsp?uuid=${res.uuid}`; resolve(accessLink); }) }) } client.subscribe("Rechnung", async function({ task, taskService }) { console.log("*** Processing task " + task.id); const uploadedFile = task.variables.get("rechnung"); const tempSavePath = uploadedFile.filename; // 等待文件从Camunda下载完成、写入本地磁盘 await downloadFile(`http://localhost:8080/engine-rest${uploadedFile.remotePath}`, tempSavePath); const uploadCmd = `curl -u okmAdmin:admin -H "Accept: application/json" -X POST -F docPath="/okm:root/manuel/${task.id}_${tempSavePath}" -F content="@${tempSavePath}" http://172.20.16.90:8080/OpenKM/services/rest/document/createSimple`; // 等待OpenKM上传完成,拿到生成的文件访问链接 const finalLink = await uploadToOpenKM(uploadCmd); const processVariables = new Variables(); processVariables.set("link", finalLink); await taskService.complete(task, processVariables); // 可选:任务完成后删除本地临时文件,避免磁盘空间占用 fs.unlink(tempSavePath, () => {}); });
额外优化建议
- 可以给异步逻辑加try/catch异常捕获,出现下载/上传失败时标记任务失败或者抛出BPMN错误,避免外部任务长时间持锁
- 不需要的依赖(比如示例中未使用的moment)及时删除,减少代码冗余
内容的提问来源于stack exchange,提问作者Asterisk
相关产品推荐
相关产品推荐

