.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     }
View Code

原理其實就是 調用了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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM