服务器将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插件;
項目目錄:
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
查看服務運行情況
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
至此,本地開發環境轉換服務成功運行。
接下來準備上服務器!!
總結
以上是生活随笔為你收集整理的服务器将office转pdf文件,Windows服务-Office转PDF文件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql 测试数据的脚本_Mysql脚
- 下一篇: Oracle函数的信息,Oracle中获