ASP.NET Convert File to Office, ODF & PDF
2022-05-04
綜整 ASP.NET 開發,如何在後端處理檔案轉換 (Convert File),從 Microsoft Office 系列檔案 Docx, Xlsx 轉換為 ODT, ODS 以及 PDF 檔案。
使用的解決方案包含 LibreOffice SDK, Microsfot 以及開源或者是收費的第三方的元件。
說明
關於多種文件格式、轉換的 .NET 解決方案
來源 | 目標 | 工具 |
---|---|---|
Data | .docx | NPOI, DataTables, RDLC, SSRS |
Data | .xlsx | NPOI, ClosedXML, DataTables, RDLC, SSRS |
Data | QuestPDF, iTextSharp, DataTables, RDLC, SSRS | |
Data | .csv | StringBuilder, DataTables, SSRS |
.docx | .odt | LibreOffice SDK, Independentsoft, NDC ODF API |
.docx | LibreOffice SDK | |
.xlsx | .ods | LibreOffice SDK, Independentsoft, NDC ODF API |
.xlsx | LibreOffice SDK | |
.ppt | LibreOffice SDK, Independentsoft, NDC ODF API | |
.ppt | LibreOffice SDK |
技術 | 技術成本 |
---|---|
StringBuilder | C# Feauters |
NPOI | 開源軟體 |
DataTables | 開源軟體,前端資料處理 |
RDLC | 微軟本機報表功能 |
SSRS | 微軟報表伺服器,需要 SQL Server 授權以及部署伺服器 |
ClosedXML | 開源軟體 |
LibreOffice SDK | 開源軟體 |
Independentsoft | 付費授權 |
NDC ODF API | 開源軟體,需要部署伺服器 |
GemBox | 付費授權 |
處理 ODF 檔案格式
說明如何使用 LibreOffice SDK 來處理轉檔為 ODF 以及 PDF 的方式。
soffice
作業系統安裝 LibreOffice 並使用 soffice.exe
進行轉檔
cd C:\Program Files (x86)\NDC ODF Application Tools 6\program
soffice --headless --convert-to odt "c:\tmp\temp.docx" --outdir "c:\tmp"
soffice --headless --convert-to pdf "c:\tmp\temp.docx" --outdir "c:\tmp"
Open Office Library
伺服器端需要安裝 LibreOffice 以及 LibreOffice SDK。
但藉由下列方式將 cli.dll 抽出,可以不需要安裝 LibreOffice SDK,參考威廉蕭在 Excel檔轉PDF產生的問題與解決方法 以及 Abdul Munim 在 HOW TO: Convert office documents to PDF using Open Office/LibreOffice in C# 所分享的方式。
將下列的 dll 複製到 VS 專案的 Library
資料夾,並且加入到 VS 專案的參考。
C:\Windows\Microsoft.NET\assembly\GAC_MSIL\cli_basetypes\v4.0_1.0.20.0__ce2cb7e279207b9e\cli_basetypes.dll
C:\Windows\Microsoft.NET\assembly\GAC_MSIL\cli_oootypes\v4.0_1.0.9.0__ce2cb7e279207b9e\cli_oootypes.dll
C:\Windows\Microsoft.NET\assembly\GAC_MSIL\cli_ure\v4.0_1.0.23.0__ce2cb7e279207b9e\cli_ure.dll
C:\Windows\Microsoft.NET\assembly\GAC_MSIL\cli_uretypes\v4.0_1.0.9.0__ce2cb7e279207b9e\cli_uretypes.dll
C:\Windows\Microsoft.NET\assembly\GAC_64\cli_cppuhelper\v4.0_1.0.23.0__ce2cb7e279207b9e\cli_cppuhelper.dll
如果只複製 LibreOffice SDK 但沒有安裝 LibreOffice 在伺服器端,會發生以下錯誤
未處理的例外狀況: System.Runtime.InteropServices.SEHException: 外部元件傳回例外狀況。
於 cppu.bootstrap(Reference<com::sun::star::uno::XComponentContext>* )
於 uno.util.Bootstrap.bootstrap()
Coding
專案會需要使用到下列的 Library。
using uno;
using uno.util;
using unoidl.com.sun.star.beans;
using unoidl.com.sun.star.frame;
using unoidl.com.sun.star.lang;
using unoidl.com.sun.star.uno;
Convert
public static bool Convert(string source, string target, string format, ref string error)
{
error = string.Empty;
if (!File.Exists(source))
{
error = "輸入的檔案不存在";
return false;
}
if (GetFilterType(Path.GetExtension(source), format) == null)
{
error = "不正確的副檔名";
return false;
}
FileInfo fileInfo = new FileInfo(source);
fileInfo.IsReadOnly = false;
XComponentContext context = Bootstrap.bootstrap();
XMultiServiceFactory factory = (XMultiServiceFactory)context.getServiceManager();
XComponentLoader aLoader =
(XComponentLoader)factory.createInstance("com.sun.star.frame.Desktop");
XComponent document = InitDocument(
aLoader,
string.Format("file:///{0}",
source.Replace("\\", "/")),
"_blank");
int seconds = 30;
while (document == null)
{
if (seconds <= 0)
{
error = $"LibreOffice 文件逾時 {seconds} 秒";
return false;
}
Thread.Sleep(1000);
seconds--;
}
target =
((target != null && target.Length != 0) ?
string.Format("file:///{0}", target.Replace("\\", "/")) :
string.Format("file:///{0}/{1}.{2}",
Path.GetDirectoryName(source).Replace("\\", "/"),
Path.GetFileNameWithoutExtension(source), format));
SaveAsDocument(document, source, target, format);
document.dispose();
return true;
}
SaveAsDocument
private static void SaveAsDocument(
XComponent xComponent, string sourceFile, string destinationFile, string format)
{
PropertyValue[] array = (PropertyValue[])(object)new PropertyValue[2]
{
(PropertyValue)(object)new PropertyValue(),
default(PropertyValue)
};
array[0].Name = "FilterName";
array[0].Value = new Any(GetFilterType(Path.GetExtension(sourceFile), format));
((XStorable)xComponent).storeToURL(destinationFile, array);
array[1] = (PropertyValue)(object)new PropertyValue();
array[1].Name = "Overwrite";
array[1].Value = new Any(true);
}
InitDocument
private static XComponent InitDocument(XComponentLoader aLoader, string file, string target)
{
PropertyValue[] array = (PropertyValue[])(object)new PropertyValue[1]
{
(PropertyValue)(object)new PropertyValue()
};
array[0].Name = "Hidden";
array[0].Value = new Any(true);
return aLoader.loadComponentFromURL(file, target, 0, array);
}
GetFilterType
private static string GetFilterType(string extension, string format)
{
switch (extension)
{
case ".doc":
case ".docx":
case ".txt":
case ".rtf":
case ".html":
case ".htm":
case ".xml":
case ".odt":
case ".wps":
case ".wpd":
switch (format)
{
case "pdf":
return "writer_pdf_Export";
case "odf":
return "writer8";
case "doc":
return "MS Word 97";
case "html":
return "HTML (StarWriter)";
default:
return null;
}
case ".xls":
case ".xlsx":
case ".ods":
switch (format)
{
case "pdf":
return "calc_pdf_Export";
case "odf":
return "calc8";
case "html":
return "HTML (StarCalc)";
default:
return null;
}
case ".ppt":
case ".pptx":
case ".odp":
switch (format)
{
case "pdf":
return "impress_pdf_Export";
case "odf":
return "draw8";
case "html":
return "impress_html_Export";
default:
return null;
}
default:
return null;
}
}
Program.cs
Convert(
@"C:\tmp\temp.docx",
@"c:\tmp\temp.odt", "odf",
ref error
);