在 IIS 環境下使用 C# 操作 LibreOffice 進行檔案轉換 問題背景 在 Web 應用程式中,我們經常需要提供檔案格式轉換的功能。本文將詳細說明如何在 IIS 環境下,使用 C# 程式碼呼叫 LibreOffice 進行檔案格式轉換(例如:Excel 轉 ODS),並完整解決執行過程中可能遇到的權限問題。
環境需求
Windows Server 環境
IIS 網頁伺服器
LibreOffice 套件
.NET Framework 開發環境
可能遇到的問題
使用一般使用者帳號無法執行轉換
IIS ApplicationPool 權限不足
檔案轉換過程沒有回應
程式執行但找不到輸出檔案
權限不足導致程式無法執行
完整解決方案 1. IIS 應用程式集區設定 首先確認 IIS 應用程式集區的設定:
開啟 IIS 管理員
找到您的應用程式集區(例如:DefaultAppPool)
右鍵 → 進階設定
設定以下項目:
載入使用者設定檔 = True
識別 = ApplicationPoolIdentity
2. Windows 使用者權限指派 設定執行檔案轉換的使用者權限:
開啟「本機安全性原則」
按 Windows + R
輸入 secpol.msc
按確定
在左側樹狀目錄中依序展開
設定下列權限(對於您要使用的帳號,例如 IIS APPPOOL\DefaultAppPool 或特定使用者帳號)
找到「以批次工作登入」(Log on as a batch job)
按右鍵 → 內容
點選「新增使用者或群組」
加入使用者帳號
找到「以服務方式登入」(Log on as a service)
按右鍵 → 內容
點選「新增使用者或群組」
加入使用者帳號
找到「允許本機登入」(Allow log on locally)
按右鍵 → 內容
點選「新增使用者或群組」
加入使用者帳號
如果使用 IIS APPPOOL\DefaultAppPool
上述三個權限都要加入 IIS APPPOOL\DefaultAppPool
特別注意「以批次工作登入」這個權限一定要加入
3. 資料夾權限設定 為執行帳號設定適當的資料夾權限:
在以下資料夾按右鍵 → 內容 → 安全性 → 編輯
點選「新增」,輸入您使用的帳號(例如:IIS APPPOOL\DefaultAppPool)
給予以下權限:
需要設定的資料夾:
LibreOffice 安裝目錄(通常是 C:\Program Files\LibreOffice)
轉換檔案的來源資料夾
轉換檔案的目標資料夾
暫存資料夾(例如:D:\temp)
4. 程式碼實作 以下是完整的程式碼實作,包含詳細的錯誤處理和記錄:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 using System;using System.Diagnostics;using System.Runtime.InteropServices;using System.ComponentModel;using System.Security;using System.IO;using System.Configuration;public class ImpersonateUser { private const int LOGON32_LOGON_INTERACTIVE = 2 ; private const int LOGON32_PROVIDER_DEFAULT = 0 ; private const int LOGON32_LOGON_BATCH = 4 ; [DllImport("advapi32.dll" , SetLastError = true) ] public static extern bool LogonUser ( string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken ) ; [DllImport("advapi32.dll" , SetLastError = true) ] public static extern bool ImpersonateLoggedOnUser (IntPtr hToken ) ; [DllImport("advapi32.dll" , SetLastError = true) ] public static extern bool RevertToSelf () ; [DllImport("kernel32.dll" , SetLastError = true) ] public static extern bool CloseHandle (IntPtr hObject ) ; public static void ConvertFile (string sourcePath, string destPath, string Format ) { string sofficePath = @"C:\Program Files\LibreOffice\program\soffice.exe" ; Process process = null ; IntPtr tokenHandle = IntPtr.Zero; try { XLogFile.WriteLog($"原始執行身份: {System.Security.Principal.WindowsIdentity.GetCurrent().Name} " ); string userName = ConfigurationManager.AppSettings["SystemAccount" ].ToString(); string password = ConfigurationManager.AppSettings["SystemPassword" ].ToString(); string domain = "." ; bool loginSuccess = LogonUser( userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, out tokenHandle); if (!loginSuccess) { int error = Marshal.GetLastWin32Error(); throw new Win32Exception(error, $"LogonUser 失敗. 錯誤碼: {error} " ); } if (!ImpersonateLoggedOnUser(tokenHandle)) { int error = Marshal.GetLastWin32Error(); throw new Win32Exception(error, "ImpersonateLoggedOnUser 失敗" ); } XLogFile.WriteLog($"切換後執行身份: {System.Security.Principal.WindowsIdentity.GetCurrent().Name} " ); ProcessStartInfo startInfo = new ProcessStartInfo(); startInfo.FileName = sofficePath; startInfo.Arguments = $"--headless --convert-to \"{Format} \" \"{sourcePath} \" --outdir \"{Path.GetDirectoryName(destPath)} \"" ; startInfo.UseShellExecute = false ; startInfo.RedirectStandardOutput = true ; startInfo.RedirectStandardError = true ; startInfo.CreateNoWindow = true ; startInfo.WorkingDirectory = Path.GetDirectoryName(sourcePath); XLogFile.WriteLog($"完整命令: {startInfo.FileName} {startInfo.Arguments} " ); process = new Process(); process.StartInfo = startInfo; process.OutputDataReceived += (sender, e) => { if (!string .IsNullOrEmpty(e.Data)) XLogFile.WriteLog($"程序輸出: {e.Data} " ); }; process.ErrorDataReceived += (sender, e) => { if (!string .IsNullOrEmpty(e.Data)) XLogFile.WriteLog($"程序錯誤: {e.Data} " ); }; process.Start(); process.BeginOutputReadLine(); process.BeginErrorReadLine(); process.WaitForExit(); XLogFile.WriteLog($"程序結束代碼: {process.ExitCode} " ); string expectedOutputFile = Path.Combine( Path.GetDirectoryName(destPath), Path.GetFileNameWithoutExtension(sourcePath) + "." + Format); if (File.Exists(expectedOutputFile)) { XLogFile.WriteLog("檔案轉換成功" ); } else { XLogFile.WriteLog("警告:找不到輸出檔案" ); } } catch (Exception ex) { XLogFile.WriteLog($"轉換過程發生錯誤: {ex.ToString()} " ); throw ; } finally { if (process != null ) { process.Close(); } RevertToSelf(); if (tokenHandle != IntPtr.Zero) { CloseHandle(tokenHandle); } } } }
5. Web.config 設定 在 Web.config 中加入使用者帳號設定:
1 2 3 4 5 6 <configuration > <appSettings > <add key ="SystemAccount" value ="您的使用者帳號" /> <add key ="SystemPassword" value ="您的密碼" /> </appSettings > </configuration >
6. 使用方式 1 2 3 4 5 6 ImpersonateUser.ConvertFile( @"D:\Source\test.xlsx" , @"D:\Destination\test.ods" , "ods" );
完整檢查清單 設定完成後,使用以下清單檢查是否已完成所有必要設定:
IIS 設定
資料夾權限
使用者權限指派
Web.config 設定
常見問題排除
程式沒有回應
檢查 IIS 應用程式集區的識別設定
確認資料夾權限設定
查看事件檢視器中的錯誤記錄
找不到輸出檔案
確認目標資料夾的寫入權限
檢查 LibreOffice 是否正確安裝
查看記錄檔中的錯誤訊息
權限不足
確認已設定正確的資料夾權限
檢查使用者帳號是否有足夠權限
確認 Web.config 中的帳號設定正確
設定完權限後仍無法執行
重新啟動 IIS
重新啟動應用程式集區
必要時重新啟動伺服器
結論 要在 IIS 環境下成功使用 LibreOffice 進行檔案轉換,關鍵在於:
正確設定 IIS 應用程式集區
設定完整的 Windows 使用者權限
設定適當的資料夾權限
實作完整的錯誤處理機制
記錄詳細的執行過程
只要依照上述步驟仔細設定,就能順利完成檔案轉換的功能。如果遇到問題,也可以透過記錄檔快速找出問題所在。
希望這篇文章能幫助遇到類似問題的開發者,快速建立起可靠的檔案轉換機制!