前言

前几年做了个项目,里面有个需求,需要在浏览器中在线浏览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应用进行转码操作即可。

踩坑记录:

接下来就是遇到的坑了

  1. 当执行第一次转码操作时,CS服务会调用OpenOffice软件,界面屏幕会弹出一个弹窗(这个弹出只会弹出一次,不会弹出了),这个弹窗内容是需要填写的基本名称,否则会导致OpenOffice一致停留在这个界面
  2. 但我们CTS服务默认是以Local System 账户运行的,而CS服务的启动是由 Windows Service 触发的, 所以OpenOffice软件其实是由Local System用户打开的,但Local System 打开没有界面弹窗的,无法填写,也就导致无法转码了。如何证明呢,看第三点。
  3. 查看任务管理器发现其实 OpenOffice 软件已经打开(进程为soffice.bin进程),而且运行用户正好就是Local System。

解决方案有两种:

  1. 新建一个Windows用户DocConverter,将该用户放到管理员组下,然后以该用户登录windows后,打开OpenOffice,第一次弹窗后 填写对应的基本信息后,将Windows Service 启动用户改为DocCoverter用户,然后再启动转码服务。 这时候会发现已经能够正常工作了。
  2. 想办法以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",说明此命令提示符已以本地系统的身份运行了。

基本上就是这样。

参考:

如何以system身份运行指定的程序?

OpenOffice

.NET 采用开源软件OpenOffice 实现文档转码服务(word ppt excel)转PDF的更多相关文章

  1. 【Python】读取各种文档(txt、csv、excel、pdf)方法

    1.读取txt文件 注意事项: 1..txt文件同下方脚本所在的.py文件需要在同一个文件夹下 # coding=utf-8 txt读取 with open("1233.txt") ...

  2. 一款开源免费的.NET文档操作组件DocX(.NET组件介绍之一)

    在目前的软件项目中,都会较多的使用到对文档的操作,用于记录和统计相关业务信息.由于系统自身提供了对文档的相关操作,所以在一定程度上极大的简化了软件使用者的工作量. 在.NET项目中如果用户提出了相关文 ...

  3. linkedin开源的kafka-monitor安装文档

    linkedin开源的kafka-monitor安装文档 linkedin 开源的kafka-monitor的安装使用可以参考官方的readme:流程介绍的已经比较清楚,但是还是有一些地方需要修正.让 ...

  4. 电脑软件安装过程文档.BA

    MD 01-打印并阅读-电脑软件安装过程文档.BAT-即此批处理脚本文档MD 02-阅读-电脑软件安装经验教训文档.DOCX-MD 03-制作-杏雨梨云USB维护系统2019中秋版之国庆更新-可启动U ...

  5. S01-晓亮的电脑软件安装过程文档 腾讯QQ 595076941 2019年10月

    S01-晓亮的电脑软件安装过程文档 腾讯QQ 595076941 2019年10月 本文档的创建作者的腾讯QQ聊天号码是 595076941 S02-电脑软件安装过程中不要随意关闭窗口除非必需关闭窗口 ...

  6. 软件基础1Word文档编辑

    word文档编辑 启动Word2010 创建文档,<你好word>. 编辑文字. 保存的三种方式. ctrl+s. 点击文件选择保存,或另存为. 快速工具栏保存按钮. 设置字体 1.通过工 ...

  7. java操作office和pdf文件java读取word,excel和pdf文档内容

    在平常应用程序中,对office和pdf文档进行读取数据是比较常见的功能,尤其在很多web应用程序中.所以今天我们就简单来看一下Java对word.excel.pdf文件的读取.本篇博客只是讲解简单应 ...

  8. C# 基于NPOI+Office COM组件 实现20行代码在线预览文档(word,excel,pdf,txt,png)

    由于项目需要,需要一个在线预览office的功能,小编一开始使用的是微软提供的方法,简单快捷,但是不符合小编开发需求, 就另外用了:将文件转换成html文件然后预览html文件的方法.对微软提供的方法 ...

  9. C# 导出word文档及批量导出word文档(4)

          接下来是批量导出word文档和批量打印word文件,批量导出word文档和批量打印word文件的思路差不多,只是批量打印不用打包压缩文件,而是把所有文件合成一个word,然后通过js来调用 ...

  10. C#在线预览文档(word,excel,pdf,txt,png)

    C#在线预览文档(word,excel,pdf,txt,png) 1.预览方式:将word文件转换成html文件然后预览html文件2.预览word文件:需要引入Interop.Microsoft.O ...

随机推荐

  1. #随机#CF1198F GCD Groups 2

    题目 将 \(n\) 个数分为两组,使得两组的GCD都为1,求具体的分组情况 分析 考虑直接打乱 \(n\) 个数,如果能使第一组GCD减小就减小,否则丢到第二组, 由于打乱后出错的概率会减小,所以r ...

  2. #构造#CF891B Gluttony

    题目 有一个长度为 \(n\) 的数列 \(a\),数字互不相同, 现在要打乱这个数列,设其为 \(b\), 使得 \(a\) 的任意真子序列与对应的 \(b\) 的任意真子序列的数字和不同 输出任意 ...

  3. RabbitMQ 07 发布订阅模式

    发布订阅模式 发布订阅模式结构图: 比如信用卡还款日临近了,那么就会给手机.邮箱发送消息,提示需要去还款了,但是手机短信和邮件发送并不一定是同一个业务提供的,但是现在又希望能够都去执行,就可以用到发布 ...

  4. 批处理及有状态等应用类型在 K8S 上应该如何配置?

    众所周知, Kubernetes(K8S)更适合运行无状态应用, 但是除了无状态应用. 我们还会有很多其他应用类型, 如: 有状态应用, 批处理, 监控代理(每台主机上都得跑), 更复杂的应用(如:h ...

  5. DevEco Device Tool 3.1 Beta1版本发布,产品化配置优化添加自定义烧录器

    原文:https://mp.weixin.qq.com/s/lVENZqc-1getmkoSgCJiEg,点击链接查看更多技术内容.   HUAWEI DevEco Device Tool(以下内容简 ...

  6. 基于QUBO模型的多体分子对接

    技术背景 本文分享内容来自于最新的一篇名为Multibody molecular docking on a quantum annealer的文章,这篇文章的核心思想,是使用QUBO(二次受限二元优化 ...

  7. ContOS7搭建RAID-5磁盘阵列

    RAID5:分布式奇偶校验的独立磁盘结构 RAID5就是raid0和RAID1的一种折中,既提升了磁盘读写能力,又有一定的容错能力,成本也低: 实验开始: 1.挂载四块5G硬盘 2.进行分区:fdis ...

  8. 重新点亮shell————awk函数[十五]

    前言 简单介绍一下awk函数. 正文 算术函数 字符串函数 自定义函数 例子: 结 awk就到这里了.

  9. redis 简单整理——内存的优化[二十七]

    前言 简单介绍一下内存的优化. 正文 Redis所有的数据都在内存中,而内存又是非常宝贵的资源.如何优化内存的使用一直是Redis用户非常关注的问题.本节深入到Redis细节中,探索内存优化的技巧. ...

  10. Java中list集合深复制

    import org.apache.commons.collections.CollectionUtils; import java.util.ArrayList; import java.util. ...