Ubuntu中Yamup部署的Meteor应用同步读写文件问题求助
解决Meteor应用中wkhtmltopdf生成临时文件的root权限问题
我之前在部署Meteor应用时也碰到过类似的权限坑,结合你的代码场景,给你几个实用的解决方案:
方案一:改用当前用户有权限的临时目录
大概率是你配置的DOWNLOADS_PATH目录属于root用户,导致生成的临时文件继承了目录的权限。直接换成Node.js提供的系统临时目录(默认对当前运行用户开放读写权限)会更省心:
const os = require('os'); const path = require('path'); // 替换原来的tmpFilePath生成逻辑 const tmpFilePath = path.join(os.tmpdir(), fileName); wkhtmltopdf(html, { pageSize: 'A4', encoding: 'utf-8' }) .pipe(fs.createWriteStream(tmpFilePath)) .on('error', (err) => { future.return({ err, buffer: null }); }) .on('close', () => { try { const buffer = fs.readFileSync(tmpFilePath); fs.unlinkSync(tmpFilePath); future.return({ err: null, buffer }); } catch (readErr) { future.return({ err: readErr, buffer: null }); } });
os.tmpdir()会自动返回适配当前系统的临时路径(比如Linux下的/tmp),完全不用操心权限问题。
方案二:生成文件后先调整权限(适合必须用原目录的场景)
如果一定要用Meteor.settings.DOWNLOADS_PATH下的tmp目录,可以在读写文件前先修改文件权限。注意:这个方法要求你的Meteor进程拥有修改该文件的权限(比如进程以root运行,或者文件所属组和进程用户一致):
.on('close', () => { try { // 给当前用户添加读写权限(0o600是八进制权限码) fs.chmodSync(tmpFilePath, 0o600); const buffer = fs.readFileSync(tmpFilePath); fs.unlinkSync(tmpFilePath); future.return({ err: null, buffer }); } catch (err) { future.return({ err, buffer: null }); } });
方案三:让wkhtmltopdf以普通用户身份运行
如果你的Meteor进程是root运行的,但你不想生成root权限的文件,可以直接用Node.js的原生child_process.spawn调用wkhtmltopdf,并指定运行的用户ID(UID)和组ID(GID)。你需要先找到服务器上普通用户的UID/GID(可以用id -u username命令查看):
const { spawn } = require('child_process'); const fs = require('fs'); const path = require('path'); const tmpFilePath = Meteor.settings['DOWNLOADS_PATH'] + '/tmp/' + fileName; // 替换成你服务器上普通用户的UID和GID,比如1000 const runAsUser = { uid: 1000, gid: 1000 }; // 直接调用系统的wkhtmltopdf二进制文件 const wkProcess = spawn('wkhtmltopdf', [ '--page-size', 'A4', '--encoding', 'utf-8', '-', // 从标准输入读取html内容 tmpFilePath ], runAsUser); // 写入html内容到进程标准输入 wkProcess.stdin.write(html); wkProcess.stdin.end(); wkProcess.on('error', (err) => { future.return({ err, buffer: null }); }); wkProcess.on('close', (exitCode) => { if (exitCode !== 0) { return future.return({ err: new Error(`wkhtmltopdf执行失败,退出码:${exitCode}`), buffer: null }); } try { const buffer = fs.readFileSync(tmpFilePath); fs.unlinkSync(tmpFilePath); future.return({ err: null, buffer }); } catch (readErr) { future.return({ err: readErr, buffer: null }); } });
优先级建议
优先选方案一,系统临时目录是最稳妥的选择,能避开大部分权限问题;如果业务限制必须用原目录,再考虑方案二;方案三适合需要严格控制进程权限的场景。
内容的提问来源于stack exchange,提问作者Lee
相关产品推荐
相关产品推荐

