服务器将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…

java实现rtsp转rtmp

RTSP转RTMP有多种实现方式&#xff0c;原理差不多。 最简单的可以用ffmpeg来实现&#xff1a; ffmpeg -i "rtsp流路径" -vcodec copy -acodec copy -f flv "rtmp://127.0.0.1:1935/live/" 上面实现了功能&#xff0c;但是没有工程化&#xff0c;实用性不…

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

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

RTSP数据流无插件浏览器播放实现

最近接的音视频处理的项目比较多&#xff0c;停下来整理一下思路&#xff0c;分享一下我踩过的坑&#xff0c;希望能够帮到别人。 我们平时接触到的视频流一般有以下几种&#xff1a; 1.RTSP(Real Time Streaming Protocol)协议&#xff0c;RTSP在体系结构上位于RTP和RTCP之上&…

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

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…

docker内存阀值_kubernetes调度之资源耗尽处理配置

本篇将介绍如何使用kubelet处理资源耗尽的情况当可用的计算机资源非常低的时候,kubelet仍然要保证节点的稳定性.当处理不可压缩的计算机资源(比如内存或磁盘空间)时,这尤其重要,当这些资源被耗尽时,节点将变得不稳定驱离策略kubelet会积极的监视并阻止可用计算机资源耗尽.这种情…

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…

python3.6和3.7的区别_python3.6和3.7有什么区别

Python 3.7 已经发布了&#xff0c;目前Python的用户&#xff0c;主要使用的版本 应该是 Python3.6 和 Python2.7 &#xff0c;那么是不是该转到 Python 3.7 呢&#xff1f;首先大家要知道Python 3.7 是对 Python 3.6 的升级。Python官方对 3.7 的新特性&#xff1a;(推荐学习&…

vue 日期格式化返回指定个数月份_12、vue中日期格式化转换的函数

/*** format time depend on the offer timestamp or Date instance* param {Number, Date } date 要转换的日期* param {String} format 字符串日期格式 default DD/MM/YYYY HH:mm* param {Boolean} instance describe 配置项, 如果为true 返回转换后的日期对象实例 …

beego 例子_beego框架代码分析

前言也许beego框架在国内应该是众多PHPer转go的首选&#xff0c;因为beego的MVC、ORM、完善的中文文档让PHPer们得心应手&#xff0c;毫无疑问我也是。这种感觉就像当年入门PHP时使用ThinkPHP一样。也许随着你的认知的提升&#xff0c;你会讨厌现在东西&#xff0c;比如某一天你…

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;它在电子…