.NET Core 使用 LibreOffice 實(shí)現(xiàn) Office 預(yù)覽(Docker 部署)
前些年做云盤產(chǎn)品的時(shí)候,一個(gè)很核心的功能就是 Office 文件預(yù)覽,當(dāng)時(shí)還沒有使用 .NET Core ,程序部署在 Windows Server 服務(wù)器上,文件預(yù)覽的方案采用了微軟的 OWA 。
目前在做的零代碼產(chǎn)品中的表單附件控件,同樣面臨著 Office 文件預(yù)覽的問題,現(xiàn)在技術(shù)棧采用了 .NET Core ,并使用容器化部署,自然就拋棄了 OWA 的方案。
本文簡(jiǎn)單介紹下 OWA 的替代方案。
思路
- 在表單的附件控件上傳 Office 文件后,存儲(chǔ)到 MongoDB 中,并發(fā)消息給文件轉(zhuǎn)換程序。
- 文件轉(zhuǎn)換程序從 MongoDB 獲取 Office 文件,通過 Libreoffice 轉(zhuǎn)換為 PDF 文件。
- 將 PDF 文件存儲(chǔ)到 MongoDB 中,并將 PDF 文件在 MongoDB 中的 FileID 存儲(chǔ)到平臺(tái)和原始文件進(jìn)行關(guān)聯(lián)。
- 在表單中點(diǎn)擊文件預(yù)覽時(shí)使用關(guān)聯(lián)的 PDF 的文件 ID 從 MongoDB 中獲取 PDF 文件進(jìn)行展示。
準(zhǔn)備
1、創(chuàng)建一個(gè) .NET Core 的控制臺(tái)程序用來做文件的轉(zhuǎn)換。
2、下載 Libreoffice 安裝包、Libreoffice 中文語言包、jdk1.8 安裝包 、中文字體包。
3、搭建一臺(tái) centos 虛擬機(jī),并準(zhǔn)備好 docker 環(huán)境。
版本
- .NET Core:3.1
- CentOS:7.6
- Docker:
- Liberoffice:7.3.5
- RabbitMQ:3.8.2
- MongoDB:5.0
開始
編寫控制臺(tái)程序進(jìn)行文件轉(zhuǎn)換
1、創(chuàng)建一個(gè)名為 OfficeToPdf 的 .NET Core 控制臺(tái)程序,在 Main 方法中對(duì)消息隊(duì)列進(jìn)行監(jiān)聽。
static void Main(string[] args)
{
try
{
var mqManager = new MQManager(new MQConfig
{
AutomaticRecoveryEnabled = true,
HeartBeat = 60,
NetworkRecoveryInterval = new TimeSpan(60),
Host = EnvironmentHelper.GetEnvValue("MQHostName"),
UserName = EnvironmentHelper.GetEnvValue("MQUserName"),
Password = EnvironmentHelper.GetEnvValue("MQPassword"),
Port = EnvironmentHelper.GetEnvValue("MQPort")
});
if (mqManager.Connected)
{
_logger.Log(LogLevel.Info, "RabbitMQ連接成功。");
_logger.Log(LogLevel.Info, "RabbitMQ消息接收中...");
mqManager.Subscribe<PowerPointConvertMessage>(Convert);
mqManager.Subscribe<WordConvertMessage>(Convert);
mqManager.Subscribe<ExcelConvertMessage>(Convert);
}
else
{
_logger.Warn("RabbitMQ連接初始化失敗,請(qǐng)檢查連接。");
Console.ReadLine();
}
}catch(Exception ex)
{
_logger.Error(ex.Message);
}
}
2、在 Convert 方法中對(duì)消息進(jìn)行處理,首先根據(jù)消息的中的文件 ID 獲取文件:
Stream sourceStream = fileOperation.GetFile(officeMessage.FileInfo.FileId);
if(sourceStream == null)
{
logger.Log(LogLevel.Error, $"文件ID:{officeMessage.FileInfo.FileId},不存在");
}
string filename = officeMessage.FileInfo.FileId;
string extension = System.IO.Path.GetExtension(officeMessage.FileInfo.FileName);
sourcePath = System.IO.Path.Combine(Directory.GetCurrentDirectory(), filename + extension);
destPath = System.IO.Path.Combine(Directory.GetCurrentDirectory(), string.Format("{0}.pdf", filename));
logger.Log(LogLevel.Info, $"文件原路徑:{sourcePath}");
logger.Log(LogLevel.Info, $"文件目標(biāo)路徑:{destPath}");
if (extension != null && (extension.Equals(".xlsx",StringComparison.OrdinalIgnoreCase) ||
extension.Equals(".xls", StringComparison.OrdinalIgnoreCase)))
{
if (!SetExcelScale(sourceStream, sourcePath))
return false;
}
else
{
byte[] sourceBuffer = new Byte[sourceStream.Length];
sourceStream.Read(sourceBuffer, 0, sourceBuffer.Length);
sourceStream.Seek(0, SeekOrigin.Begin);
if (!SaveToFile(sourceBuffer, sourcePath))
return false;
}
3、啟用 LibreOffice 進(jìn)行文件轉(zhuǎn)換:
var psi = new ProcessStartInfo(
"libreoffice7.3",
string.Format("--invisible --convert-to pdf {0}", filename + extension))
{RedirectStandardOutput = true};
// 啟動(dòng)
var proc = Process.Start(psi);
if (proc == null)
{
logger.Error("請(qǐng)檢查 LibreOffice 是否成功安裝.");
return false;
}
logger.Log(LogLevel.Info, "文件轉(zhuǎn)換開始......");
using (var sr = proc.StandardOutput)
{
while (!sr.EndOfStream)
{ Console.WriteLine(sr.ReadLine());
} if (!proc.HasExited)
{ proc.Kill();
}}
logger.Log(LogLevel.Info, "文件轉(zhuǎn)成完成");
4、文件轉(zhuǎn)換成功后,存儲(chǔ)轉(zhuǎn)換后的 PDF 文件到 MongoDB,然后和原始文件進(jìn)行關(guān)聯(lián),下面代碼是調(diào)用了零代碼平臺(tái)中的接口進(jìn)行處理,這里可以根據(jù)自己的業(yè)務(wù)需求自行修改 :
string host = EnvironmentHelper.GetEnvValue("ApiHost");
string api = EnvironmentHelper.GetEnvValue("AssociationApi");
if (string.IsNullOrEmpty(api))
{
logger.Warn("請(qǐng)檢查 AssociationApi 環(huán)境變量的配置");
return false;
}
if (string.IsNullOrEmpty(host))
{
logger.Warn("請(qǐng)檢查 ApiHost 環(huán)境變量的配置");
return false;
}
string result = APIHelper.RunApiGet(host, $"{api}/{fileId}/{destFileId}");
構(gòu)建 Libreoffice 基礎(chǔ)鏡像
1、在 centos 服務(wù)器上 /data 目錄中創(chuàng)建目錄 liberoffice-docker-build ,將上面提到的 Libreoffice 安裝包、Libreoffice 中文語言包、jdk1.8 安裝包 、中文字體包拷貝到該目錄中。
2、在該目錄中創(chuàng)建 Dockerfile 文件,內(nèi)容如下:
RUN yum update -y && \
yum reinstall -y glibc-common && \
yum install -y telnet net-tools && \
yum clean all && \
rm -rf /tmp/* rm -rf /var/cache/yum/* && \
localedef -c -f UTF-8 -i zh_CN zh_CN.UTF-8 && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
#加入windows字體包
ADD chinese.tar.gz /usr/share/fonts/
ADD LibreOffice_7.3.5_Linux_x86-64_rpm.tar.gz /home/
ADD LibreOffice_7.3.5_Linux_x86-64_rpm_langpack_zh-CN.tar.gz /usr/
#執(zhí)行安裝
RUN cd /home/LibreOffice_7.3.5.2_Linux_x86-64_rpm/RPMS/ \
&& yum localinstall *.rpm -y \
&& cd /usr/LibreOffice_7.3.5.2_Linux_x86-64_rpm_langpack_zh-CN/RPMS/ \
&& yum localinstall *.rpm -y \
#安裝依賴
&& yum install ibus -y \
#加入中文字體支持并賦權(quán)限
&& cd /usr/share/fonts/ \
&& chmod -R 755 /usr/share/fonts \
&& yum install mkfontscale -y \
&& mkfontscale \
&& yum install fontconfig -y \
&& mkfontdir \
&& fc-cache -fv \
&& mkdir /usr/local/java/ \
#清理緩存,減少鏡像大小
&& yum clean all
#安裝java環(huán)境
ADD jdk-8u341-linux-x64.tar.gz /usr/local/java/
RUN ln -s /usr/local/java/jdk1.8.0_314 /usr/local/java/jdk
#配置環(huán)境變量
ENV JAVA_HOME /usr/local/java/jdk
ENV JRE_HOME ${JAVA_HOME}/jre
ENV CLASSPATH .:${JAVA_HOME}/lib:${JRE_HOME}/lib
ENV PATH ${JAVA_HOME}/bin:$PATH
#安裝 dotnet core 3.1 運(yùn)行環(huán)境
RUN rpm -Uvh https://packages.microsoft.com/config/centos/7/packages-microsoft-prod.rpm \
&& yum install -y aspnetcore-runtime-3.1 \
&& yum clean all
WORKDIR /usr
EXPOSE 80
CMD /bin/bash
3、執(zhí)行命令 docker build -t libreofficebase:v1.0 . 進(jìn)行基礎(chǔ)鏡像的構(gòu)建,構(gòu)建好的基礎(chǔ)鏡像供文件預(yù)覽鏡像構(gòu)建時(shí)使用。
構(gòu)建文件預(yù)覽鏡像
1、在 centos 服務(wù)器的 /data 目錄中創(chuàng)建目錄 doc-preview-docker-build 。
2、將轉(zhuǎn)換程序 OfficeToPdf 進(jìn)行編譯發(fā)布,將發(fā)布后的文件拷貝到目錄 doc-preview-docker-build 中。
3、在該目錄中創(chuàng)建 Dockerfile 文件,內(nèi)容如下:
FROM libreofficebase:v1 #此處的鏡像就是上面構(gòu)建的 Libreoffice 基礎(chǔ)鏡像
COPY . /app
WORKDIR /app
EXPOSE 80/tcp
ENTRYPOINT ["dotnet", "OfficeToPdf.dll"]
4、執(zhí)行命令 docker build -t office-preview:v1.0 . 進(jìn)行預(yù)覽鏡像的構(gòu)建。
運(yùn)行預(yù)覽容器
執(zhí)行下面命令進(jìn)行容器的創(chuàng)建:
docker run -d --name office-preview office-preview
最后
Office 預(yù)覽肯定有很多種方案,上面只是目前找到的一種可行的方法。