Linux环境下Runtime.getRuntime().exec执行Calibre转MOBI失败求助
问题描述
在Linux开发机上通过Runtime.getRuntime().exec()调用Calibre的ebook-convert命令将HTML转换为MOBI时失败,生成的MOBI文件为空。该功能在Windows 10本地正常运行,尝试用{"bash", "-c", command}的方式执行命令仍无改善,且读取进程输入流无日志输出。
执行的命令示例:
/usr/src/calibre/ebook-convert /tmp/filesDirectory_mobi3970575619159760977/d7ed6792-b3cb-4761-bb6a-b9facf9e7a6c9250643820137116550.html /tmp/filesDirectory_mobi3970575619159760977/d7ed6792-b3cb-4761-bb6a-b9facf9e7a6c11909891397415910433.mobi
相关代码片段:
@Override public Document convert(Document document, DocumentFormat documentFormat) { Document htmlDocument = htmlDocumentConverter.convert(document, documentFormat); try { log.info("Converting document from {} to {}", getSourceFormat().toString(), getTargetFormat().toString()); CalibreConfigData calibreData = calibreConfig.getConfigurationData(CalibreConversion.HTML_TO_MOBI); Files.write(calibreData.getSourceFilePath(), htmlDocument.getContent()); String command = calibreData.getCalibreCommand(); String[] cmd = {"bash", "-c", command}; var r = Runtime.getRuntime().exec(cmd); r.waitFor(); log.warn("Process: " + new String(r.getInputStream().readAllBytes(), StandardCharsets.UTF_8)); byte[] convertedFileAsBytes = Files.readAllBytes(calibreData.getConvertedFilePath()); // Files.deleteIfExists(calibreData.getSourceFilePath()); // Files.deleteIfExists(calibreData.getConvertedFilePath()); // Files.deleteIfExists(calibreData.getFilesDirectoryPath()); return new Document(convertedFileAsBytes); } catch (InterruptedException | IOException e) { log.error("Conversion failed due to problem: " + e); throw new ConversionException("Conversion failed due to problem: " + e); } }
排查与解决步骤
1. 读取进程错误输出流
当前代码仅读取进程标准输出流,但ebook-convert的错误信息通常输出到错误流,导致无法获取失败原因。修改代码同时读取错误流:
var r = Runtime.getRuntime().exec(cmd); // 先读取错误流,避免缓冲区阻塞进程 String errorOutput = new String(r.getErrorStream().readAllBytes(), StandardCharsets.UTF_8); int exitCode = r.waitFor(); String output = new String(r.getInputStream().readAllBytes(), StandardCharsets.UTF_8); log.warn("Process exit code: {}", exitCode); log.warn("Process output: {}", output); log.error("Process error: {}", errorOutput);
这一步能直接获取转换失败的具体原因,比如权限不足、依赖缺失、HTML格式错误等。
2. 手动在终端执行命令验证
直接在Linux终端运行给出的ebook-convert命令,确认是否能生成正常MOBI文件:
/usr/src/calibre/ebook-convert /tmp/filesDirectory_mobi3970575619159760977/d7ed6792-b3cb-4761-bb6a-b9facf9e7a6c9250643820137116550.html /tmp/filesDirectory_mobi3970575619159760977/d7ed6792-b3cb-4761-bb6a-b9facf9e7a6c11909891397415910433.mobi
如果手动执行失败,说明是Calibre配置或HTML文件本身的问题;如果手动执行成功,再排查Java进程的执行环境。
3. 检查Java进程权限
确保运行Java程序的用户对以下路径有读写权限:
- HTML临时文件路径(
/tmp/filesDirectory_mobi...) - MOBI输出路径
- Calibre安装目录(
/usr/src/calibre/)
可通过ps aux | grep java找到Java进程的运行用户,再用sudo -u <用户名> 执行上述ebook-convert命令验证权限是否足够。
4. 直接调用命令而非通过bash包装
如果命令没有复杂shell语法(如管道、通配符),直接拆分命令为数组调用,避免shell层的参数转义问题:
String[] cmd = { "/usr/src/calibre/ebook-convert", calibreData.getSourceFilePath().toString(), calibreData.getConvertedFilePath().toString() }; var r = Runtime.getRuntime().exec(cmd);
5. 检查Calibre版本与依赖
Linux上的Calibre版本可能与Windows不同,部分HTML语法在旧版本中可能不兼容。执行/usr/src/calibre/ebook-convert --version查看版本,必要时升级到稳定版。同时确保系统安装了Calibre依赖的库(如Debian系的libfontconfig1、libxrender1等)。
6. 验证HTML文件格式
即使HTML文件有内容,也可能存在格式问题导致Calibre无法解析。尝试用简单测试HTML(如仅包含<h1>Test</h1>)进行转换,确认是否是原HTML的问题。
内容的提问来源于stack exchange,提问作者me_how

