springboot整合libreoffice(两种方式,使用本地和远程的libreoffice);do ...

打印 上一主题 下一主题

主题 802|帖子 802|积分 2406

一、 配景

因为项目中需要使用word转pdf功能,因为转换速度原因,最后选用了libreoffice,原因及部署请参考
linux ubuntu环境安装libreoffice,word转pdf
远程调用的话可选docker部署,请看2.3.1
二、springboot整合libreoffice

其实springboot整合libreoffice有两种方式,一种是使用本地的libreoffice,一种是使用远程服务的libreoffice(这个好多文章中没有提到,也是本身踩的坑算是)
2.1、整合本地服务

引入pom
  1.     <dependency>
  2.                 <groupId>org.jodconverter</groupId>
  3.                 <artifactId>jodconverter-spring-boot-starter</artifactId>
  4.                 <version>4.4.2</version>
  5.             </dependency>
  6.             <dependency>
  7.                 <groupId>org.jodconverter</groupId>
  8.                 <artifactId>jodconverter-local</artifactId>
  9.                 <version>4.4.2</version>
  10.             </dependency>
复制代码
yml配置
  1. jodconverter:
  2.   local:
  3.     enabled: true
  4.     #window地址:  D:\\workplaces\\jcxx\\libreoffice 请自行补全
  5.     #linux地址:   /opt/libreoffice24.2
  6.     office-home: /opt/libreoffice24.2
  7.     # 端口(线程)
  8.     portNumbers: [8101,8102,8103]
  9.     maxTasksPerProcess: 100
  10.     # 任务执行的超时时间
  11.     task-execution-timeout: 360000
  12.     # 任务队列的超时时间
  13.     task-queue-timeout: 360000
  14.     # 一个进程的超时时间
  15.     process-timeout: 360000
复制代码
使用也很简单
  1.     @Resource
  2.     private DocumentConverter documentConverter;
  3.    public void test() {
  4.    
  5. //流转换
  6.    documentConverter.convert(inputStream).as(DefaultDocumentFormatRegistry.DOCX).to(outStream).as(DefaultDocumentFormatRegistry.PDF).execute();
  7.    //文件转换,sourceFile和targetFile都是File类实例
  8.     documentConverter.convert(sourceFile).to(targetFile).as(DefaultDocumentFormatRegistry.PDF).execute();
  9.    }
复制代码
2.2、整合远程服务

说一下怎么发现的,学过springboot的应该都知道,整合其他服务时候应该都有个配置类xxxAutoConfiguration

于是发现了除了一个local外,还有个remote,才发现可以直接调用远程服务,发现了那就可以整合使用,如下
pom引入
  1.   <dependency>
  2.                 <groupId>org.jodconverter</groupId>
  3.                 <artifactId>jodconverter-spring-boot-starter</artifactId>
  4.                 <version>4.4.2</version>
  5.             </dependency>
  6.            <dependency>
  7.                 <groupId>org.jodconverter</groupId>
  8.                 <artifactId>jodconverter-remote</artifactId>
  9.                 <version>4.4.2</version>
  10.             </dependency>
复制代码
yml配置
  1. jodconverter:
  2.   remote:
  3.     enabled: true
  4.     url: http://192.168.1.16:8100
  5.     ssl:
  6.       enabled: false
复制代码
注意,一定要加http,我就不小心忽略了这个东西,只写了个ip+端口,结果导致同等报错
  1. java.net.MalformedURLException: no protocol
复制代码
以下可不看,直接看踩坑
使用方式和local的一样,参考上面
不外比local方式多了一步,要手动启动远程的libreoffice服务
启动下令,附上对应下令的寄义
https://help.libreoffice.org/latest/zh-CN/text/shared/guide/start_parameters.html
  1. soffice --headless --nologo --nofirststartwizard --norestore --accept="socket,host=0.0.0.0,port=8100;urp;" &
复制代码
以为到此就竣事了吗?不不不,是我想的太简单了
对了,上面的下令还踩了一些坑,百度的时候都是127.0.0.1,想telnet通的的话需要使用0.0.0.0,不外,纠结这个似乎没啥意义,原因似乎不在这里(当时还花了很久排查)
不消0.0.0.0的话,直接报错连不上对应ip端口,改了之后报下面的错
2.3、整合远程服务踩坑

当我在服务器上运行该下令后,满心欢喜的等着转换完成时,突然转换就卡住了,随后报错
  1. org.jodconverter.core.office.OfficeException: Remote conversion failed
  2.         at org.jodconverter.remote.task.RemoteConversionTask.execute(RemoteConversionTask.java:162)
  3.         at org.jodconverter.remote.office.RemoteOfficeManagerPoolEntry.doExecute(RemoteOfficeManagerPoolEntry.java:301)
  4.         at org.jodconverter.core.office.AbstractOfficeManagerPoolEntry.lambda$execute$0(AbstractOfficeManagerPoolEntry.java:80)
  5.         at java.util.concurrent.FutureTask.run(FutureTask.java:266)
  6.         at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
  7.         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  8.         at java.lang.Thread.run(Thread.java:748)
  9. Caused by: java.net.SocketTimeoutException: Read timed out
  10.         at java.net.SocketInputStream.socketRead0(SocketInputStream.java)
  11.         at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
  12.         at java.net.SocketInputStream.read(SocketInputStream.java:171)
  13.         at java.net.SocketInputStream.read(SocketInputStream.java:141)
  14.         at org.apache.http.impl.io.SessionInputBufferImpl.streamRead(SessionInputBufferImpl.java:137)
  15.         at org.apache.http.impl.io.SessionInputBufferImpl.fillBuffer(SessionInputBufferImpl.java:153)
  16.         at org.apache.http.impl.io.SessionInputBufferImpl.readLine(SessionInputBufferImpl.java:280)
  17.         at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:138)
  18.         at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:56)
  19.         at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:259)
  20.         at org.apache.http.impl.DefaultBHttpClientConnection.receiveResponseHeader(DefaultBHttpClientConnection.java:163)
  21.         at org.apache.http.impl.conn.CPoolProxy.receiveResponseHeader(CPoolProxy.java:157)
  22.         at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:273)
  23.         at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:125)
  24.         at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:272)
  25.         at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186)
  26.         at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
  27.         at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
  28.         at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
  29.         at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
  30.         at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
  31.         at org.apache.http.client.fluent.Request.internalExecute(Request.java:173)
  32.         at org.apache.http.client.fluent.Executor.execute(Executor.java:262)
  33.         at org.jodconverter.remote.task.RemoteConversionTask.execute(RemoteConversionTask.java:147)
  34.         ... 6 more
复制代码
连接超时?我telnet一下,端口通啊!最最让我恶心的是,我上周快下班时候使用这种方式乐成转换了一次,这就给我一种错觉,大概是我启动下令的题目,于是就疯狂实验修改启动下令,找对应的参数。结果都没卵用。。。。。。于是想上周是不是搞错了,让我误以为如允许行?
结果还真是,下令行启动压根就不能使用remote。不外没找到上周为啥乐成的原因?也忘了上周咋乐成的了。。。
https://github.com/jodconverter/jodconverter/wiki/LibreOffice-Remote

看到这我就懵了,我还得去安装个Collabora Online 或者LibreOffice Online?(当前,这也是可行的)。我看了下这两东西基本就是属于web端的在线编辑word了。。。
于是又开始找,不外这回学智慧白点,只在github中的issues中找,搜刮关键词remote
还真让我找出来两种办法:但是我只乐成了一种
对了,扔几个issues链接,有兴趣的可以看下
  1. https://github.com/jodconverter/jodconverter/wiki/Migration-Guide-4.4.5
  2. https://github.com/jodconverter/jodconverter/issues/40
  3. https://github.com/jodconverter/jodconverter/issues/350
  4. https://github.com/jodconverter/jodconverter/issues/397
复制代码
2.3.1、方法一(乐成)

也就是https://github.com/jodconverter/jodconverter/issues/397这个里面提到的方案。代码很简单,都可以看下,简单来说就是本地启动一个libreoffice服务,并对外提供接口调用(就一个controller)
docker镜像拉不下来参考这个,我是看第一点乐成的https://blog.csdn.net/weixin_50160384/article/details/139861337
jodconverter提供了一个远程服务的接口,我们可以直接docker运行
  1. docker run -d -p 8100:8100 --privileged=true -v /usr/share/fonts:/usr/share/fonts -v /opt/application.properties:/etc/app/application.properties ghcr.io/jodconverter/jodconverter-examples:rest
复制代码
挂载对应字体,否则中文不表现
-v /usr/share/fonts:/usr/share/fonts
挂载配置文件,用于修改端口等
-v /opt/application.properties:/etc/app/application.properties
配置文件在这看https://github.com/jodconverter/docker-image-jodconverter-examples
对应文件我也贴下
  1. # amount of libreOffice instances to start - one for each given port. So this means 2
  2. jodconverter.local.port-numbers: 2002, 2003
  3. # change the tmp folder
  4. jodconverter.local.working-dir: /tmp
  5. # change upload sizes
  6. spring.servlet.multipart.max-file-size: 5MB
  7. spring.servlet.multipart.max-request-size: 5MB
  8. # change the server port (where the REST app is listenting
  9. server.port=8100
复制代码
然后这个的ip+端口号使用jodconverter.remote方式就乐成了
也许有同学已经安装好了libreoffice,想着我这libreoffice不白装了,最后用docker。。。
其实也不然,可以本身将项目打个包放到服务器去运行,不外这个需要本身研究下了
https://github.com/jodconverter/jodconverter-samples
不外我看了下dockerfile文件,大概是这个下令(没用过gradlew )

2.3.2、使用local参数,但是使用远程服务器(失败)

根据https://github.com/jodconverter/jodconverter/wiki/Migration-Guide-4.4.5描述及https://github.com/jodconverter/jodconverter/issues/40,似乎可以使用local来进行访问
于是,有了如下配置
  1. jodconverter:
  2.   local:
  3.     enabled: true
  4.           office-home: D:\\workplaces\\libreoffice
  5.            port-numbers: 9999
  6.            load-document-mode: remote
  7.            start-fail-fast: true
  8.           host-name: 120.46.141.243
复制代码
有一点很让人费解,使用了remote模式,还必须配置office-home。。。我使用远程服务上的所在还不行。。。还有一点,必须要提前启动位于服务器上的9999端口服务,不提前启动步调启动不起来。
然后我实验了下,固然也能转换乐成,但是用了40多秒。
不清楚是什么原因,办理的小搭档可以在品评区讨论下
  1. soffice --headless --nologo --nofirststartwizard --norestore --accept="socket,host=0.0.0.0,port=9999;urp;" &
复制代码
三、docker中同时部署应用和libreoffice(不保举)

单独部署libreoffice的可以本身再查询下,这里就不先容了
因为一开始我只发现了local这种方式,以是就在window本地和linux服务器上都部署了下,厥后突然想到,丫的服务是用的docker进行部署的。docker里面又没有libreoffice,还访问个屁呀,我linux部署上没啥用啊!
意识到这点后,起首实验着把Linux中的libreoffice挂载到docker容器中,但以交互模式进去后
soffice 照旧libreoffice24.2都执行不了。。。。
那想着只能将libreoffice也弄到容器中去了。。。
于是有了下面的dockerfile文件
  1. # 使用基于 Alpine 的 OpenJDK 镜像
  2. FROM registry.cn-beijing.aliyuncs.com/hub-mirrors/openjdk:8-jdk-alpine
  3. # 更新包列表并安装必要的软件
  4. RUN apk add --no-cache bash libreoffice
  5. # 复制 jar 文件到容器
  6. COPY xxx.jar app.jar
  7. COPY fonts/zhFonts /usr/share/fonts
  8. # 设置环境变量
  9. ENV JAVA_HOME=/usr/lib/jvm/default-jvm
  10. ENV LIBREOFFICE_HOME=/usr/lib/libreoffice
  11. ENV PATH=$JAVA_HOME/bin:$LIBREOFFICE_HOME/program:$PATH
  12. # 设置 ENTRYPOINT 以允许使用 exec
  13. ENTRYPOINT ["/bin/bash", "-c"]
  14. # 设置 CMD 以启动 Java 应用
  15. CMD ["java -Djava.security.egd=file:/dev/./urandom -jar app.jar"]
  16. #LibreOffice 6.1.4.2 版本
复制代码
也许对docker照旧不太认识,在copy下令的时候源文件似乎不能使用绝对路径,这个让我有点奇怪,
于是把东西全都挪到了/opt目录下

然后执行下令,注意最后有个.
  1. docker build -t新镜像名字:TAG.
复制代码
不保举的原因就是在于此,一个镜像高达1g。。。
实测后libreoffice和服务都是正常的,可以接受镜像大和构建时间长些的也可以使用这种方式

发现这点后,我感觉这也太low了,究竟之前jar包也就将近200m,而且使用的是阿里的云效流水线构建工具,如许构建一次得花多久?(没有去公司服务器实验,本身用云服务器实验,第一次构建dockerfile拉取libreoffice花了得10多分钟,第二次用dockerfile构建就是秒拉取了,大概也只是第一次慢?不知道用云效如何?有兴趣的可以实验下)。
实验着找别的方法办理,于是无意中发现了JodConverterRemoteAutoConfiguration,对应配置类
JodConverterRemoteProperties
四、其他题目

4.1、使用远程的libreoffice时候用excel转pdf的时候样式庞杂

原来是如许的格式

变成了如许

反正发现这个题目我是很懵圈的。但没办法,排查呗。
实验下本地的libreoffice另存为是正常的。
实验下代码本地的libreoffice也是正常的。
实验下inux的本地的libreoffice也是正常的。
然后我就开始猜疑是不是remote源码层面的题目,于是去把remote的代码弄到本地试了下,也是正常的。
然后我就想着用本身买的服务器试试,是不是application配置文件的题目,但是,我发现,我本身服务器的远程服务是正常的。
那我就意识到了,都是docker容器,不一样的只能是挂载文件,那题目就只能出在字体上了,因为用服务器试过word转pdf没有中文乱码题目(之前有人上传了一些字体),以是服务器上我就没有上传字体,于是就出现了该题目,字体照旧要上传全的。
题目就出在字体上,上传上去字体就好了

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

兜兜零元

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表