如果超过最大允许时间,如何取消将 ms office 文件转换为 PDF 的 Libreoffice UNO API 库过程

Posted

技术标签:

【中文标题】如果超过最大允许时间,如何取消将 ms office 文件转换为 PDF 的 Libreoffice UNO API 库过程【英文标题】:How to cancel Libreoffice UNO API Library process of convertion of ms office files to PDF in case it is over maximum permitted time 【发布时间】:2019-05-09 21:08:20 【问题描述】:

我正在使用 Libreoffice 6.0 中的 UNO API 库 (Soffice) 将 ms office 格式转换为 PDF,Soffice 进程在服务器模式下处理多个 sumultanious 请求。 通常转换速度很快,但在转换一些大文件时,例如xlsx 或 pptx,Soffice 进程使用 100% CPU,转换最多需要几分钟。 这是不可接受的,因为在此期间并发请求不会被处理。

为了处理这种情况,我尝试使用 java.util.concurrent 将一些子任务作为线程执行,并通过未来接口进行超时控制。但只有在转换的原始 ms office 文档加载阶段发生挂起时,它才能正常工作。 如果转换过程已经开始,即使发生超时异常,Soffice 进程也不会立即退出 100% 加载,而是继续将文档转换为 pdf。 程序执行暂停尝试处理加载的文档。

linux下通过命令启动SOffice进程:

Runtime.getRuntime().exec("/usr/lib64/libreoffice/program/soffice, --nologo, --nodefault, --norestore, --nocrashreport, --nolockcheck, --accept=socket,host=localhost,port=8100;urp;");

ms office 文件转换为简体pdf的代码为:

public  void  convertFile()
xRemoteContext = BootstrapSocketConnector.bootstrap(oooPath);
xRemoteServiceManager = xRemoteContext.getServiceManager();
Object desktop = null;
desktop = xRemoteServiceManager.createInstanceWithContext("com.sun.star.frame.Desktop", xRemoteContext);
xComponentLoader = (XComponentLoader) UnoRuntime.queryInterface(XComponentLoader.class, desktop);
File mfile = new File(workingDir + myTemplate);
String sUrl = pathToURL(workingDir + myTemplate);
PropertyValue[] propertiesIn;
propertiesIn = new PropertyValue[2];
propertiesIn[0] = property("ReadOnly", Boolean.TRUE);
propertiesIn[1] = property("Hidden", Boolean.TRUE);
XComponent xComp = null;
try 
//xComp = xComponentLoader.loadComponentFromURL(sUrl, "_blank", 0, propertiesIn);
//The same via timeout control
xComp = callLibreLoad(sUrl, propertiesIn);

catch (TimeoutException ex) 
if(xComp!= null)
xComp.dispose();
...

// save as a PDF
XStorable xStorable = (XStorable) UnoRuntime.queryInterface(XStorable.class, xComp);
PropertyValue[] propertiesOut = new PropertyValue[2];
propertiesOut[0] = property("FilterName", formatfilter);
propertiesOut[1] = property("Overwrite", Boolean.TRUE);
String myResult = workingDir + fileNameOut;
try 
//xStorable.storeToURL(pathToURL(myResult), propertiesOut);
//The same via timeout control
callLibreStore(xStorable,pathToURL(myResult), propertiesOut);

catch (TimeoutException ex) 
if(xComp!= null)
 xComp.dispose();
...

if(xComp!= null)
 xComp.dispose();

函数 callLibreLoad 和 callLibreStore 使用 Future 接口进行超时控制:

private XComponent callLibreLoad(String sUrl, PropertyValue[] propertiesIn) throws Exception 
XComponent result = null;
ExecutorService executor = Executors.newCachedThreadPool();
Callable<Object> task = new Callable<Object>() 
 public Object call() throws IllegalArgumentException, com.sun.star.io.IOException 
 return xComponentLoader.loadComponentFromURL(sUrl, "_blank", 0, propertiesIn);
 
;
Future<Object> future = executor.submit(task);
try 
 result = (XComponent) future.get(maxTimeout, TimeUnit.SECONDS); 
 
finally 
 future.cancel(true); 
return result;


private void callLibreStore(XStorable xStorable, String sUrl, PropertyValue[] propertiesOut) throws Exception 
Integer result = null;
ExecutorService executor = Executors.newCachedThreadPool();
Runnable task = new Runnable() 
public void run() 
try 
xStorable.storeToURL(sUrl, propertiesOut);
 catch (com.sun.star.io.IOException e) 
log.error(e);


;
Future future = executor.submit(task);
try 
future.get(maxTimeout, TimeUnit.SECONDS); 

finally 
future.cancel(true); // may or may not desire this
   

所以,当函数 callLibreLoad 发生超时异常时,SOffice 进程立即恢复到工作状态。 但是当稍后发生超时时,在函数 callLibreStore 中,即使发生超时并且转换线程被中断后,SOffice 进程仍处于 100% 加载状态超过一分钟,试图处理加载的文档,执行代码 xComp.dispose()。 此时Java线程与SOffice进程的堆栈跟踪包含以下内容:

State: WAITING on com.sun.star.lib.uno.environments.remote.JobQueue@30af74b8
Total blocked: 455  Total waited: 1 967
Stack trace: 
java.lang.Object.wait(Native Method)
com.sun.star.lib.uno.environments.remote.JobQueue.removeJob(JobQueue.java:207)
com.sun.star.lib.uno.environments.remote.JobQueue.enter(JobQueue.java:316)
com.sun.star.lib.uno.environments.remote.JobQueue.enter(JobQueue.java:289)
com.sun.star.lib.uno.environments.remote.JavaThreadPool.enter(JavaThreadPool.java:81)
com.sun.star.lib.uno.bridges.java_remote.java_remote_bridge.sendRequest(java_remote_bridge.java:618)
com.sun.star.lib.uno.bridges.java_remote.ProxyFactory$Handler.request(ProxyFactory.java:145)
com.sun.star.lib.uno.bridges.java_remote.ProxyFactory$Handler.invoke(ProxyFactory.java:129)
com.sun.proxy.$Proxy211.close(Unknown Source)
com.componentplus.prom.libreoffice.LibreOfficeStationary.closeDocument(LibreOfficeStationary.java:425)
com.componentplus.prom.libreoffice.LibreOfficeStationary.convertFile(LibreOfficeStationary.java:393)
...

如果需要超过最大允许时间,如何强制 Soffice 取消转换为 pdf。

【问题讨论】:

很明显,文件转换线程是在另一个进程(soffice.bin)上启动的,不可能中断外部进程的那个线程。所以我想这个问题没有决定性。 【参考方案1】:

一种可能性可能是保存Runtime.getRuntime().exec 返回的Process 实例,例如在变量myProc 中,然后在需要时调用myProc.destroy() 杀死进程。

【讨论】:

我试图销毁 Process=Runtime.getRuntime().exec(/usr/lib64/libreoffice/program/soffice, --nologo, --nodefault, --norestore, --nocrashreport, - -nolockcheck, --accept=socket,host=localhost,port=8100;urp;) Soffice.bin 保留在进程列表中,并且在被杀死之前无法使用。 无赖。您可能会在其中找到其他有用的信息,但答案基于 Oracle 页面上的 Runtime 和 Process 类信息。 刚刚意识到一些事情...我认为应该是Process myProc = Runtime.getRuntime().exec(...) 而不是Process=Runtime...,然后是myProc.destroy()。但这可能是您已经尝试过的,我只是误解了您的评论。 如你所见,soffice.bin 进程以套接字模式启动。另外soffice.bin不是java进程,当你调用Process myProc = Runtime.getRuntime().exec(...)启动libreoffice时,myProc变量不是soffice.bin进程,而是一些辅助进程,我们在破坏myProc不要破坏 soffice.bin 进程。 但最糟糕的是,在通过 xComponentLoader.loadComponentFromURL 加载文档并通过 xStorable.storeToURL 开始文档转换后,soffice.bin 进程仍然忙于转换,直到它完成,即使您尝试以所有可能的方式中止它,包括销毁 myProc 或通过可用的接口方法关闭文档。

以上是关于如果超过最大允许时间,如何取消将 ms office 文件转换为 PDF 的 Libreoffice UNO API 库过程的主要内容,如果未能解决你的问题,请参考以下文章

OpenOffice 和 MS Office 格式转换为 TIFF

Microsoft office2016版产品密钥的激活次数达到了最大允许次数'这应该怎么解决啊

AWS EBS 错误:源包为空或超过允许的最大大小:524288000

如何查找当前打开的 MS Office 文档的名称?

如何管理打开的数据报套接字以避免超过最大值?

如何修复 PHP 警告:file_exists():文件名超过此平台上允许的最大路径长度 (260)?