服务器将office转pdf文件,Windows服务-Office转PDF文件

一. 应用场景

开发一个课件在线学习功能,要求将WORD, EXCEL, PPT类型课件可在线打开学习;最初设想使用第三方office插件,无奈价格太高放弃使用;

我们最终的方案是:利用office自身的另存为功能,在服务器将上传的office文件转化为pdf格式,然后网页打开pdf文件实现在线学习功能;

项目实现方案:新建一个windows自启的service安装在服务器,这个服务里面会建立一个HttpListener,用于监听文件转换的请求,请求来了后交给ServiceHandle处理,ServiceHandle会调用ConvertHelper把指定目录下的office文件转化为pdf,并放在相同目录下,以供在线学习使用;

二. 工具及环境

VS2019

Office:本地开发2016版;服务器2010版

开发环境 win10

服务器环境 windows server 2012 R2

VS开发使用到的nuget包:NetOffice

三. 创建windows server项目

基本按照这个思路开发出一个基础service完全没问题;这里需要提醒下的是:项目的文件路径不要带有空格,否则在执行bat批处理操作时会出现问题。

四. 使用NetOffice实现文件转换

上面只是创建一个服务, 真正转换的核心功能还没开始,这时我们需要利用NetOffice插件;

项目目录:

be2d0a3a2b4f

image.png

利用nuget包管理器安装所需包,这是项目的packages.config文件:

转换核心代码:ConvertHelper.cs

public class ConvertHelper

{

public static void ConvetToPdf(string sourcePath, string targetPath)

{

if (!File.Exists(sourcePath))

{

throw new Exception(string.Format("文件{0}不存在", sourcePath));

}

if (File.Exists(targetPath))

{

throw new Exception(string.Format("目标文件{0}已存在", targetPath));

}

LogHelper.Info("开始转换:" + Path.GetFileName(sourcePath));

var targetDirectory = Path.GetDirectoryName(targetPath);

if (!Directory.Exists(targetDirectory))

{

Directory.CreateDirectory(targetDirectory);

}

var ext = Path.GetExtension(sourcePath).ToLower();

if (ext == ".ppt" || ext == ".pptx")

{

ConvertPptToPdf(sourcePath, targetPath);

}

else if (ext == ".doc" || ext == ".docx")

{

ConvertDocumentToPdf(sourcePath, targetPath);

}

else if (ext == ".xls" || ext == ".xlsx")

{

ConvertExcelToPdf(sourcePath, targetPath);

}

else

{

LogHelper.Info("{0}不支持转换pdf", sourcePath);

return;

}

if (File.Exists(targetPath))

{

LogHelper.Info("转换成功:" + Path.GetFileName(sourcePath));

}

else

{

LogHelper.Info("转换失败:" + Path.GetFileName(sourcePath));

}

}

///

/// 转换ppt文件到pdf文件(不支持超时终止)

///

///

///

private static void ConvertPptToPdf(string sourcePath, string targetPath)

{

NetOffice.PowerPointApi.Application application = null;

NetOffice.PowerPointApi.Presentation presentation = null;

var cts = new CancellationTokenSource();

var thread = new Thread(() =>

{

try

{

application = new NetOffice.PowerPointApi.Application();

presentation = application.Presentations.Open(sourcePath, true, NetOffice.OfficeApi.Enums.MsoTriState.msoTrue, false);

presentation.SaveCopyAs(targetPath, NetOffice.PowerPointApi.Enums.PpSaveAsFileType.ppSaveAsPDF);

}

catch (Exception ex)

{

if (ex.InnerException != null && ex.InnerException is ThreadAbortException)

{

LogHelper.Info("ConvertPptToPdf: {0}操作超时", Path.GetFileName(sourcePath));

}

else

{

LogHelper.Error(ex, "ConvertPptToPdf: {0}出现异常", Path.GetFileName(sourcePath));

}

}

finally

{

if (presentation != null)

{

presentation.Close();

presentation = null;

}

if (application != null)

{

application.Quit();

application.Dispose();

application = null;

}

//killProccess("POWERPNT");

}

});

cts.Token.Register(() =>

{

thread.Abort();

});

cts.CancelAfter(Program.MaxThreads * 1000);

thread.Start();

thread.Join();

GC.Collect();

GC.WaitForPendingFinalizers();

}

///

/// 转换word文件到pdf文件(支持超时终止)

///

///

///

private static void ConvertDocumentToPdf(string sourcePath, string targetPath)

{

NetOffice.WordApi.Application application = null;

NetOffice.WordApi.Document document = null;

var cts = new CancellationTokenSource();

var thread = new Thread(() =>

{

try

{

application = new NetOffice.WordApi.Application();

document = application.Documents.Open(sourcePath);

document.ExportAsFixedFormat(targetPath, NetOffice.WordApi.Enums.WdExportFormat.wdExportFormatPDF);

}

catch (Exception ex)

{

if (ex.InnerException != null && ex.InnerException is ThreadAbortException)

{

LogHelper.Info("ConvertDocumentToPdf: {0}操作超时", Path.GetFileName(sourcePath));

}

else

{

LogHelper.Error(ex, "ConvertDocumentToPdf: {0}出现异常", Path.GetFileName(sourcePath));

}

}

finally

{

if (document != null)

{

document.Close();

document = null;

}

if (application != null)

{

application.Quit();

application.Dispose();

application = null;

}

//killProccess("WINWORD");

}

});

cts.Token.Register(() =>

{

thread.Abort();

});

cts.CancelAfter(Program.MaxThreads * 1000);

thread.Start();

thread.Join();

GC.Collect();

GC.WaitForPendingFinalizers();

}

private static void ConvertExcelToPdf(string sourcePath, string targetPath)

{

NetOffice.ExcelApi.Application application = null;

NetOffice.ExcelApi.Workbook wookbook = null;

var cts = new CancellationTokenSource();

var thread = new Thread(() =>

{

try

{

application = new NetOffice.ExcelApi.Application();

wookbook = application.Workbooks.Open(sourcePath);

wookbook.ExportAsFixedFormat(NetOffice.ExcelApi.Enums.XlFixedFormatType.xlTypePDF, targetPath);

}

catch (Exception ex)

{

if (ex.InnerException != null && ex.InnerException is ThreadAbortException)

{

LogHelper.Info("ConvertExcelToPdf: {0}操作超时", Path.GetFileName(sourcePath));

}

else

{

LogHelper.Error(ex, "ConvertExcelToPdf: {0}出现异常", Path.GetFileName(sourcePath));

}

}

finally

{

if (wookbook != null)

{

wookbook.Close();

wookbook = null;

}

if (application != null)

{

application.Quit();

application.Dispose();

application = null;

}

//killProccess("EXCEL");

}

});

cts.Token.Register(() =>

{

thread.Abort();

});

cts.CancelAfter(Program.MaxThreads * 1000);

thread.Start();

thread.Join();

GC.Collect();

GC.WaitForPendingFinalizers();

}

public static void CleanProccess()

{

LogHelper.Info("CleanProccess Start");

killProccess("WINWORD");

killProccess("EXCEL");

LogHelper.Info("CleanProccess Finish");

}

private static void killProccess(string appName)

{

// Store all running process in the system

Process[] runingProcess = Process.GetProcesses();

for (int i = 0; i < runingProcess.Length; i++)

{

// compare equivalent process by their name

if (string.Equals(runingProcess[i].ProcessName, appName, StringComparison.OrdinalIgnoreCase))

{

try

{

// kill running process

runingProcess[i].Kill();

LogHelper.Info("Kill {0} [{1}]", appName, runingProcess[i].Id);

}

catch (Exception ex)

{

if (ex is System.InvalidOperationException)

{

//进程已经关闭

}

else

{

LogHelper.Error(ex, "Kill {0} [{1}] Error", appName, runingProcess[i].Id);

}

}

}

}

}

}

ServiceListener监听http请求,端口号默认8091;

public ServiceListener()

{

_stop = new ManualResetEvent(false);

_idle = new ManualResetEvent(false);

_busy = new Semaphore(Program.MaxThreads, Program.MaxThreads);

_listener = new HttpListener();

_listenerThread = new Thread(HandleRequests);

}

public void Start()

{

var url = String.Format(@"http://localhost:{0}/", Program.ListenerPort); // Port=8091

LogHelper.Info("Listenning Start:" + url);

_listener.Prefixes.Add(url);

_listener.Start();

_listenerThread.Start();

}

五. 系统调用http服务,转换文件

public void ConvertToPdf(string serviceUrl, string sourcePath, string pdfPath)

{

var url = $"{serviceUrl}?srcPath={sourcePath}&tarPath={pdfPath}";

using (var webClient = new WebClient())

{

var result = webClient.DownloadString(url);

}

}

至此,开发完毕,本地开发完成;

六. 本地测试功能

注册服务,执行注册服务.bat

启动服务,执行启动服务.bat

查看服务运行情况

be2d0a3a2b4f

image.png

但是测试转换时遇到了问题,日志错误为 "Office 检测到该文件有问题。为帮助保护您的计算机,此文件无法打开。":

2020-05-02 09:04:22:248 Info [Thread9] 开始转换:a10a54166224453abdd8d4b809f15449.ppt

2020-05-02 09:04:25:379 Exception [Thread10] ConvertPptToPdf: a10a54166224453abdd8d4b809f15449.ppt出现异常 Exception:

System.Runtime.InteropServices.COMException (0x80004005): See inner exception(s) for details. ---> System.Reflection.TargetInvocationException: 调用的目标发生了异常。 ---> System.Runtime.InteropServices.COMException: Presentations.Open : Office 检测到该文件有问题。为帮助保护您的计算机,此文件无法打开。

--- 内部异常堆栈跟踪的结尾 ---

在 System.RuntimeType.InvokeDispMethod(String name, BindingFlags invokeAttr, Object target, Object[] args, Boolean[] byrefModifiers, Int32 culture, String[] namedParameters)

在 System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams)

在 NetOffice.Invoker.MethodReturn(COMObject comObject, String name, Object[] paramsArray)

在 NetOffice.Invoker.MethodReturn(COMObject comObject, String name, Object[] paramsArray)

在 NetOffice.PowerPointApi.Presentations.Open(String fileName, Object readOnly, Object untitled, Object withWindow)

在 LMS.DocumentConvertService.ConvertHelper.LMS.DocumentConvertService\Service\ConvertHelper.cs:行号 75

2020-05-02 09:04:33:477 Info [Thread9] 转换失败:a10a54166224453abdd8d4b809f15449.ppt

我才用了其中2种较简单解决方案:

修改代码,在ProjectInstaller.cs中ProjectInstaller类中重载OnAfterInstall

[RunInstaller(true)]

public partial class ProjectInstaller : System.Configuration.Install.Installer

{

public ProjectInstaller()

{

InitializeComponent();

}

protected override void OnAfterInstall(IDictionary savedState)

{

try

{

base.OnAfterInstall(savedState);

System.Management.ManagementObject myService = new System.Management.ManagementObject(

string.Format("Win32_Service.Name='{0}'", this.serviceInstaller1.ServiceName));

System.Management.ManagementBaseObject changeMethod = myService.GetMethodParameters("Change");

changeMethod["DesktopInteract"] = true;

System.Management.ManagementBaseObject OutParam = myService.InvokeMethod("Change", changeMethod, null);

}

catch (Exception)

{

}

}

}

SC程序修改, 允许与桌面进行交互

用批处理的方式实现,加入如下命令到>启动服务.bat文件中

sc config MonitorService type=interact type=own

至此,本地开发环境转换服务成功运行。

接下来准备上服务器!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/552589.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

最拼爹的css属性:z-index失效情况记录

最近自己用angular封装了一个多级下拉菜单的控件,使用过程中发现一个奇怪的bug 第一反应是菜单z-index太小,被覆盖了,结果设置成9999都没有效果。 调试样式后发现,是因为引用菜单控件的时候,父元素的position为fixed,右边控件的position为relative,而菜单父元素没有设置…

ubuntu nginx添加站点配置失效处理

最近开始接触阿里云服务器&#xff0c;一步步开始自己配置&#xff0c;记录下自己的一步一坑&#xff1a; nginx安装很简单&#xff0c;apt-get install nginx 成功后就可以打开默认站点 nginx配置文件在/etc/nginx目录下的nginx.conf 查看里面的文件内容可以知道&#xff0…

中用BBP公式计算_如何计算基础代谢率

了解基础代谢率&#xff08;BMR&#xff09;以后&#xff0c;可以知道身体减重、保持重量或增重时需要多少卡路里。基础代谢是身体休息的时候用掉的能量&#xff0c;即便没有动&#xff0c;身体还是在使用能量&#xff0c;维持体温、输送血液、呼吸、消化食物等等。基础代谢可以…

把类成员改成指针_如果类中存在管理其他类对象的指针,通过析构函数释放它们...

C.33: If a class has an owning pointer member, define a destructorC.33:如果类包含拥有所有权的指针成员&#xff0c;定义析构函数Reason(原因)An owned object must be deleted upon destruction of the object that owns it.从属对象必须通过拥有该对象的所有者类的析构函…

MySQL 服务无法启动--服务没有报告任何错误 ---Failed to find valid data directory.

系统重装后重新安装mysql环境&#xff0c;各种踩坑。服务无法启动 mysqld --console 查看详细错误&#xff1a;Failed to find valid data directory 发现mysql目录没有data文件夹&#xff0c;创建后问题依旧。 移除mysql&#xff0c; mysqld -remove mysql 重新初始化 mys…

hadoop fs命令无法使用_Hadoop从入门到入土(三)HDFS集群简单维护及JAVA客户端连接HDFS...

集群简单维护查看日志上篇我们搭建了集群&#xff0c;并且完成了hdfs的搭建&#xff0c;但在我的linux02和linux03上&#xff0c;发现一个问题&#xff0c;我启动了datanode之后&#xff0c;开始进程还在&#xff0c;一会就自己消失了&#xff0c;这是为什么呢&#xff1f;(如下…

c++ 函数返回空_Python all() 函数

all() 函数用于判断给定的可迭代参数 iterable 中的所有元素是否都为 TRUE&#xff0c;如果是返回 True&#xff0c;否则返回 False。元素除了是 0、空、None、False 外都算 True。语法all(iterable) # iterable -- 元组或列表。示例>>> all([a, b, c, d]) # 列表li…

kali2020识别不到网卡_WIN10环境下台式机找不到RealTek HD管理器解决耳塞式耳机外放问题...

最近想在工作休息闲暇时开个音乐播放器或者小游戏放松一下&#xff0c;需要戴上耳塞式耳机&#xff0c;但是最近这个耳机在公司台式电脑突然识别不到了&#xff0c;如果是自己的笔记本电脑在控制面板 - 硬件和声音里是可以找到“RealTek高清晰音频管理器”选项的&#xff0c;然…

韵乐x5效果器ktv最佳参数_家庭ktv(卡拉ok)家庭影院ktv选购推荐攻略

因为陆陆续续的给小区的邻居有推荐安装家庭KTV设备&#xff0c;根据他们的使用反馈和选购前遇到的问题&#xff0c;我做了汇总并整理了这篇文章. &#xff08;不写复杂的参数&#xff0c;不堆积商品推荐&#xff0c;只推荐几款不同价位&#xff0c;真正性能优质、价格美丽的家庭…

cat3 utp是不是网线_小科普 | 网线也有高低?聊聊网线的差别

好电脑、好主板、好网卡、好运营商、好套餐&#xff0c;是不是就意味着有了极好的网络体验呢&#xff1f;对&#xff0c;但也未必。因为还少算了最后一道关卡——网线。▌线材网线这个东西&#xff0c;看着还真都差不多&#xff0c;插上就能用这能有什么差别呢&#xff1f;首先…

redis续期_面试官:Redis分布式锁如何解决锁超时问题的?

一、前言关于redis分布式锁, 查了很多资料, 发现很多只是实现了最基础的功能, 但是, 并没有解决当锁已超时而业务逻辑还未执行完的问题, 这样会导致: A线程超时时间设为10s(为了解决死锁问题), 但代码执行时间可能需要30s, 然后redis服务端10s后将锁删除, 此时, B线程恰好申请锁…

markdown如何设置图片大小_不会吧,还不会用markdown排版吗

请使用 Chrome 浏览器。请阅读下方文本熟悉工具使用方法&#xff0c;本文可直接拷贝到微信中预览。1 Markdown Nice 简介支持自定义样式的 Markdown 编辑器支持微信公众号、知乎和稀土掘金欢迎扫码回复「排版」加入用户群2 主题https://preview.mdnice.com/themes/欢迎提交主题…

此次边路调整系统推荐射手走哪路_王者荣耀地图重大对称改动,终于能射手对射手,上单对上单了...

兄弟萌&#xff0c;一直困扰大家的地图问题终于要改了&#xff0c;王者峡谷将在今年的周年庆版本&#xff0c;迎来一次玩法体验的大升级&#xff01;调整介绍我们将尝试改造边路的地形&资源&#xff0c;促进更合理的同职业分路对线。以往通过职业、英雄的调整确实很难实质改…

二阶振荡环节的谐振频率_困惑了很久的串联/并联谐振电路详解

在含有电阻、电感和电容的交流电路中&#xff0c;电路两端电压与其电流一般是不同相的&#xff0c;若调节电路参数或电源频率使电流与电源电压同相&#xff0c;电路呈电阻性&#xff0c;称这时电路的工作状态为谐振。谐振现象是正弦交流电路的一种特定现象&#xff0c;它在电子…

安装不成功_iOS12.4.1 安装失败?教你百分百不掉签

说重点&#xff1a;近日&#xff0c;苹果频繁封杀证书&#xff0c;导致企业证书很缺&#xff0c;补上又封&#xff0c;想越狱又不能安装&#xff0c;实在困扰了很久&#xff0c;今天就分享百分百安装成功的方法&#xff0c;安装后配合自动续签插件&#xff0c;简直完美。接下来…

指定的参数已超出有效值的范围_整流二极管的特性和重要参数

一、 二极管的半导体原理略二、 二极管的特性曲线研究电路也有两种方法&#xff0c;一个是从它的内部工作原理去分析&#xff0c;另一个是从它表现出的外部特征去分析&#xff0c;把电路看成一个黑匣子&#xff0c;不管它内部什么结构什么原理&#xff0c;只研究它表现出来的特…

count返回0_MySQL实战 | 14 为什么count(*)越来越慢?

select count(*) 应该是一个比较常用的语句&#xff0c;用来统计记录行数。但是&#xff0c;慢慢地你会发现&#xff0c;这个语句越来越慢了&#xff0c;为什么呢&#xff1f;count(*) 的实现方式首先&#xff0c;我们来看下它的实现方式。MySQL 中&#xff0c;不同的存储引擎&…

sql管理:索引超出范围必须为非负值并小于集合大小_java面试基础知识-数据库基础知识(数据库索引部分)...

1.1数据库架构&#xff1a;如何设计一个关系型数据库?存储模块&#xff08;文件系统&#xff09;用块或者页作为存储单位 程序实例&#xff1a;存储管理缓存机制 不宜过大&#xff0c;要有淘汰机制SQL解析日志管理权限划分容灾机制索引管理锁管理1.2 索引为什么要使用索引&…

编译原理语法分析_斯坦福大学《编译原理》学习记录 - 第二章:语法分析器

05-01: Introduction to Parsing 语法分析parser的作用&#xff1a;05-02: Context Free Grammars 上下文无关文法CFG&#xff08;上下文无关文法&#xff09;回答了一个字符串是否属于某语言&#xff1a;CFG的组成&#xff1a;终止符非终止符开始符生成式生成式production &am…

mysql永远不用utf8_永远不要在 MySQL 中使用「utf8」

题图&#xff1a;by apple from Instagram本文出自极客邦「聊聊架构」公众号的编译。我自己当年也被这个问题坑过&#xff0c;当时并没有如此详细的分析文章。我觉得有责任再次分享一下&#xff0c;让更多人知道这个事情。程序员不帮助程序员&#xff0c;还能指望谁呢&#xff…