.NET 采用开源软件OpenOffice 实现文档转码服务(word ppt excel)转PDF
前言
前几年做了个项目,里面有个需求,需要在浏览器中在线浏览word excel ppt pdf等文档。
最近又开始研究并记录下来
当时方案:
- 因为浏览器中阅读文档暂时只能通过pdf方式读取,所以就要想办法实现 word excel ppt 转为pdf文件实现在线浏览。
- 考虑到文件的安全性问题,一些在线的Saas服务就不考虑了,定制化本地安装的saas服务又不现实。
- .net 中已有一些组件可以实现word 转pdf了 如aspose.net , spire.doc for .net 等等,不过这些都是收费的。
- 微软的Office 也有提供com组件实现文档转码服务,前提是必须在Windows服务器上安装Office, 但Office同样需要license
- 考虑到成本问题。
最后采用了开源 OpenOffice +OpenOffice SDK 部署在Windows服务器中实现该需求
必要前提:
- 在windows服务器 framework 4 因为是好些年前的项目了,当时采用的是.net framework 4.6.1, Linux系统倒是没试过。
- OpenOffice 软件
- OpenOffice SDK 必须保证版本一致,否则会有问题。
正文:
以下是两个中间件服务
| 服务类型 | 服务名称 | 简称 | 描述 |
| Windows Service | Convert trigger Service | CTS | 目的是来监控输入文件夹,当文件夹{InputFolder}中存在文件后,会出发转码操作。 |
| Windows Console App | Convert Service | CS | 执行转码操作,会将{InputFolder}文件夹下的文件进行转码,并放置到{OutputFolder}目录下。 |
CTS服务采用Process类 调用CS 服务
以下是物理架构的关系图:

下面是CS服务中执行转码的核心代码。

1 public class OpenOfficeHelper : IOpenOffice
2 {
3 // For thread safety
4 private Mutex _openOfficeLock;
5
6 /// <summary>
7 /// constructor
8 /// </summary>
9 public OpenOfficeHelper()
10 {
11 _openOfficeLock = new Mutex(false, "OpenOfficeMutexLock-MiloradCavic");
12 }
13
14 /// <summary>
15 /// Converts document to PDF
16 /// </summary>
17 /// <param name="sourcePath">Path to document to convert(e.g: C:\test.doc)</param>
18 /// <param name="destinationPath">Path on which to save PDF (e.g: C:\test.pdf)</param>
19 /// <returns>Path to destination file if operation is successful, or Exception text if it is not</returns>
20 public void ConvertDocToPDF(string sourcePath, string destinationPath)
21 {
22 bool obtained = _openOfficeLock.WaitOne(60 * 1000, false);
23
24 XComponent xComponent = null;
25 try
26 {
27 if (!obtained)
28 {
29 throw new System.Exception(string.Format("Request for using OpenOffice wasn't served after {0} seconds. Aborting...", 30));
30 }
31
32 sourcePath = PathConverter(sourcePath);
33 destinationPath = PathConverter(destinationPath);
34
35 // 载入文件前属性设定,设定文件开启时隐藏
36 PropertyValue[] loadDesc = new PropertyValue[1];
37 loadDesc[0] = new PropertyValue();
38 loadDesc[0].Name = "Hidden";
39 loadDesc[0].Value = new uno.Any(true);
40
41 //Get a ComponentContext
42 unoidl.com.sun.star.uno.XComponentContext xLocalContext = uno.util.Bootstrap.bootstrap();
43
44 //Get MultiServiceFactory
45 unoidl.com.sun.star.lang.XMultiServiceFactory xRemoteFactory = (unoidl.com.sun.star.lang.XMultiServiceFactory)xLocalContext.getServiceManager();
46
47 //Get a CompontLoader
48 XComponentLoader aLoader = (XComponentLoader)xRemoteFactory.createInstance("com.sun.star.frame.Desktop");
49
50 //Load the sourcefile
51 xComponent = aLoader.loadComponentFromURL(sourcePath, "_blank", 0, new unoidl.com.sun.star.beans.PropertyValue[0]);
52
53 //Wait for loading
54 while (xComponent == null)
55 {
56 Thread.Sleep(3000);
57 }
58
59 SaveDocument(xComponent, destinationPath);
60
61 xComponent.dispose();
62
63 }
64 catch (System.Exception ex)
65 {
66 throw ex;
67 }
68 finally
69 {
70 Process[] pt = Process.GetProcessesByName("soffice.bin");
71 if (pt != null && pt.Length > 0)
72 {
73 foreach (var item in pt)
74 {
75 item.Kill();
76 }
77 }
78 if (obtained)
79 {
80 _openOfficeLock.ReleaseMutex();
81 }
82 }
83 }
84
85 /// <summary>
86 /// 执行保存
87 /// </summary>
88 /// <param name="xComponent">The x component.</param>
89 /// <param name="filePath">Name of the file.</param>
90 private void SaveDocument(XComponent xComponent, string filePath)
91 {
92 unoidl.com.sun.star.beans.PropertyValue[] propertyValue = new unoidl.com.sun.star.beans.PropertyValue[1];
93
94 propertyValue[0] = new unoidl.com.sun.star.beans.PropertyValue();
95 propertyValue[0].Name = "FilterName";
96 propertyValue[0].Value = new uno.Any("writer_pdf_Export");
97
98 ((XStorable)xComponent).storeToURL(filePath, propertyValue);
99 }
100
101 /// <summary>
102 /// Convert into OO file format
103 /// </summary>
104 /// <param name="file">The file.</param>
105 /// <returns>The converted file</returns>
106 private static string PathConverter(string file)
107 {
108 try
109 {
110 file = file.Replace(@"\", "/");
111
112 return "file:///" + file;
113 }
114 catch (System.Exception ex)
115 {
116 throw ex;
117 }
118 }
119
120
121 }
原理其实就是 调用了OpenOffice 软件,另存为成PDF文件。
CTS服务的代码就不放出来,其实就是起一个Timer 定时器,定时监控 {InputFolder}文件夹下是否存在待转码文件, 存在,则起一个Process 实例 执行CS应用进行转码操作即可。
踩坑记录:
接下来就是遇到的坑了
- 当执行第一次转码操作时,CS服务会调用OpenOffice软件,界面屏幕会弹出一个弹窗(这个弹出只会弹出一次,不会弹出了),这个弹窗内容是需要填写的基本名称,否则会导致OpenOffice一致停留在这个界面


- 但我们CTS服务默认是以Local System 账户运行的,而CS服务的启动是由 Windows Service 触发的, 所以OpenOffice软件其实是由Local System用户打开的,但Local System 打开没有界面弹窗的,无法填写,也就导致无法转码了。如何证明呢,看第三点。


- 查看任务管理器发现其实 OpenOffice 软件已经打开(进程为soffice.bin进程),而且运行用户正好就是Local System。

解决方案有两种:
- 新建一个Windows用户DocConverter,将该用户放到管理员组下,然后以该用户登录windows后,打开OpenOffice,第一次弹窗后 填写对应的基本信息后,将Windows Service 启动用户改为DocCoverter用户,然后再启动转码服务。 这时候会发现已经能够正常工作了。
- 想办法以Local System用户身份打开一次OpenOffice,然后填写OpenOffice的基本信息即可,怎么打开呢,这里借助PsTools工具,以cmd命令行模式打开即可, 下载PSTools,ps工具包 点我下载
(1)打开压缩包,将里面的psexec.exe复制到System32文件夹下(64位用户请将psexec64.exe复制到SysWOW64文件夹下)
(2)以管理员身份运行命令提示符,输入"psexec -i -d -s cmd.exe"(64位用户类似),等待1~2秒后,就会出现以system权限运行的命令提示符了
(3)在被启动的命令提示符里输入命令"whoami"并回车,会发现返回一条信息为"nt authority\system",说明此命令提示符已以本地系统的身份运行了。


基本上就是这样。
参考:
.NET 采用开源软件OpenOffice 实现文档转码服务(word ppt excel)转PDF的更多相关文章
- 【Python】读取各种文档(txt、csv、excel、pdf)方法
1.读取txt文件 注意事项: 1..txt文件同下方脚本所在的.py文件需要在同一个文件夹下 # coding=utf-8 txt读取 with open("1233.txt") ...
- 一款开源免费的.NET文档操作组件DocX(.NET组件介绍之一)
在目前的软件项目中,都会较多的使用到对文档的操作,用于记录和统计相关业务信息.由于系统自身提供了对文档的相关操作,所以在一定程度上极大的简化了软件使用者的工作量. 在.NET项目中如果用户提出了相关文 ...
- linkedin开源的kafka-monitor安装文档
linkedin开源的kafka-monitor安装文档 linkedin 开源的kafka-monitor的安装使用可以参考官方的readme:流程介绍的已经比较清楚,但是还是有一些地方需要修正.让 ...
- 电脑软件安装过程文档.BA
MD 01-打印并阅读-电脑软件安装过程文档.BAT-即此批处理脚本文档MD 02-阅读-电脑软件安装经验教训文档.DOCX-MD 03-制作-杏雨梨云USB维护系统2019中秋版之国庆更新-可启动U ...
- S01-晓亮的电脑软件安装过程文档 腾讯QQ 595076941 2019年10月
S01-晓亮的电脑软件安装过程文档 腾讯QQ 595076941 2019年10月 本文档的创建作者的腾讯QQ聊天号码是 595076941 S02-电脑软件安装过程中不要随意关闭窗口除非必需关闭窗口 ...
- 软件基础1Word文档编辑
word文档编辑 启动Word2010 创建文档,<你好word>. 编辑文字. 保存的三种方式. ctrl+s. 点击文件选择保存,或另存为. 快速工具栏保存按钮. 设置字体 1.通过工 ...
- java操作office和pdf文件java读取word,excel和pdf文档内容
在平常应用程序中,对office和pdf文档进行读取数据是比较常见的功能,尤其在很多web应用程序中.所以今天我们就简单来看一下Java对word.excel.pdf文件的读取.本篇博客只是讲解简单应 ...
- C# 基于NPOI+Office COM组件 实现20行代码在线预览文档(word,excel,pdf,txt,png)
由于项目需要,需要一个在线预览office的功能,小编一开始使用的是微软提供的方法,简单快捷,但是不符合小编开发需求, 就另外用了:将文件转换成html文件然后预览html文件的方法.对微软提供的方法 ...
- C# 导出word文档及批量导出word文档(4)
接下来是批量导出word文档和批量打印word文件,批量导出word文档和批量打印word文件的思路差不多,只是批量打印不用打包压缩文件,而是把所有文件合成一个word,然后通过js来调用 ...
- C#在线预览文档(word,excel,pdf,txt,png)
C#在线预览文档(word,excel,pdf,txt,png) 1.预览方式:将word文件转换成html文件然后预览html文件2.预览word文件:需要引入Interop.Microsoft.O ...
随机推荐
- #二分图,并查集#洛谷 6185 [NOI Online #1 提高组] 序列
题目 分析 考虑2操作可以在保证总和不变的情况下任意修改, 如果将2操作所在的连通块用并查集缩点,那么再考虑1操作, 按照1操作建边,如果存在奇环,那么只要这个环的点权和为偶数一定能使 \(a,b\) ...
- #第一类斯特林数,NTT#CF960G Bandit Blues
题目 给你三个正整数 \(n\),\(a\),\(b\),定义 \(A\) 为一个排列中是前缀最大值的数的个数, 定义 \(B\) 为一个排列中是后缀最大值的数的个数,求长度为 \(n\) 的排列中满 ...
- C/C++ 项目构建指南:如何使用 Makefile 提高开发效率
Makefile是一个常用的自动化构建工具,它可以为开发人员提供方便的项目构建方式.在C/C++项目中,Makefile可以用来编译.链接和生成可执行文件.使用Makefile的好处是可以自动执行一系 ...
- Go 语言数组基础教程 - 数组的声明、初始化和使用方法
数组用于在单个变量中存储相同类型的多个值,而不是为每个值声明单独的变量. 声明数组 在Go中,有两种声明数组的方式: 使用var关键字: 语法 var array_name = [length]dat ...
- C#_面试题2
1 :维护数据库的完整性.一致性.你喜欢用触发器还是自写业务逻辑?为什么答:尽可能用约束(包括CHECK.主键.唯一键.外键.非空字段)实现,这种方式的效率最好:其次用触发器,这种方式可以保证无论何种 ...
- scala 生成指定日期范围的list
可以通过scala中的流处理,生成指定范围内的日期list import java.time.LocalDate def dateStream(fromDt:LocalDate):Stream[Loc ...
- leetcode:1381. 设计一个支持增量操作的栈
1381. 设计一个支持增量操作的栈 请你设计一个支持下述操作的栈. 实现自定义栈类 CustomStack : CustomStack(int maxSize):用 maxSize 初始化对象,ma ...
- pageSpy - 远程调试利器
视频版: https://www.bilibili.com/video/BV1Zi4y167TZ 前言 在工作中, 经常需要面对的问题就是处理客户提出的bug. 但是这个事儿最耗费精力甚至决定能不能修 ...
- State 和 Props的理解以及区别
一.state 一个组件的显示形态可以由数据状态和外部参数所决定,而数据状态就是state,一般在 constructor 中初始化 当需要修改里面的值的状态需要通过调用setState来改变,从而达 ...
- vue+scss混合(mixins)使用(css代码的vuex(公共管理))
scss混合(mixins)使用 例一.使用混合mixins中的变量来定义一个n行文本溢出隐藏的公用样式. 1.创建mixins.scss文件 //文本n行溢出隐藏 @mixin ellipsisBa ...