基於WPSOffice+Pywpsrpc構建Docker映象,實現檔案轉換和線上預覽服務

2023-12-07 18:00:31

背景

產品功能需要實現標準檔案的線上預覽功能,由於DOC檔案沒辦法直接通過瀏覽器開啟預覽,需要提前轉換為PDF檔案或者HTML頁面。

經過測試發現DOC轉為HTML頁面後檔案提交較大,而且生成的靜態資原始檔較多,需要額外搭建Web容器存放,所以還是考慮轉換為PDF格式檔案。

 

選型

需求明確後就要考慮如何實現了,這裡對比了開原始檔轉換工具例如:kkFileViewdocuments4jpoiopenofficeasposeSpire.Office等。

選型主要考慮以下幾個方面:

1、轉換為PDF檔案後是否會失真,也就是和原DOC檔案的格式還原度;

2、是否支援大檔案,有些標準檔案超過40M用MSOffice開啟很卡;

3、工具收費情況。

簡單說下結論:

1、上面幾個工具全部驗證了一遍,後發現都不太合適,原因有二,免費工具會失真或者限制了檔案的大小,收費的工具可以用但是貴;

2、最後選擇用WPS進行檔案格式轉換,原因很簡單,首先WPS有個人版是可以免費使用的,其實WPS轉換PDF檔案的還原度相當高,另外客戶方已經採購了WPS的企業版本,如果後續檔案轉換失真也有個說法;

3、解決方案:基於CentOS安裝WPS,通過Python的Pywpsrpc元件呼叫WPS的檔案轉換功能,對外提供檔案轉換服務。

 

映象構建

服務映象已構建完成,如有需要可以聯絡筆者V:hsky23557544

基礎映象

由於涉及安裝的元件太多,編寫Dockerfile的話太麻煩也不便於試錯,所以決定先拉取一個基礎的CentOS映象,把軟體服務都安裝好,最後通過docker commit將容器打成映象。

1、基礎映象docker pull centos:7.6.1810

2、啟動容器docker run --name centos --network host -dit --privileged=true centos:7.6.1810 /usr/sbin/init

3、更新到國內的YUM源,加速後面的軟體安裝,網上教學很多不增加篇幅

安裝CentOS桌面

pywpsrpc需要依賴系統桌面呼叫WPS的API,如需headless執行可以參考:https://github.com/timxx/pywpsrpc/wiki/Run-on-Server

yum -y install epel-release
yum groupinstall "GNOME Desktop"
systemctl isolate graphical.target
systemctl set-default graphical.target
yum install xrdp -y
yum install tigervnc-server -y
# 設定vnc存取密碼
vncpasswd root

systemctl start xrdp
systemctl enable xrdp

WPSOffice安裝

1、官網Linux版本安裝包下載:https://linux.wps.cn/

2、安裝yum localinstall wps-office-11.1.0.11711-1.x86_64.rpm

安裝WPSOffice依賴的字型

 推薦使用開源專案安裝:https://github.com/dv-anomaly/ttf-wps-fonts

git clone https://github.com/iamdh4/ttf-wps-fonts.git

cd ttf-wps-fonts

sudo sh install.sh

rm -rf /tmp/ttf-wps-fonts

安裝Python3.6

開始用Python3.9版本安裝pywpsrpc的時候直接報錯了,查閱了一些pywpsrpc的issues發現需要使用Python3.6.x版本,另外pywpsrpc沒有arm版本需要自行編譯whl安裝。

1、下載安裝包:https://registry.npmmirror.com/-/binary/python/3.6.15/Python-3.6.15.tgz

2、安裝依賴:yum install -y gcc-c++ pcre pcre-devel zlib zlib-devel openssl openssl--devel

3、編譯安裝

# 解壓壓縮包
tar -zxvf Python-3.6.15.tgz  

# 進入資料夾
cd Python-3.6.15

# 設定安裝位置
./configure prefix=/usr/local/python3

# 安裝
make && make install

# 建立軟連線
ln -s /usr/local/python3/bin/python3.6 /usr/bin/python3
ln -s /usr/local/python3/bin/pip3.6 /usr/bin/pip3

4、安裝pywpsrpc:pip3 install pywpsrpc -i https://pypi.tuna.tsinghua.edu.cn/simple 

5、安裝flask:pip3 install flask -i https://pypi.tuna.tsinghua.edu.cn/simple

如果提示使用pip安裝依賴包時提示SSL證書驗證的問題,需要重新編譯安裝openssl,操作如下:

# 下載原始碼包
wget http://www.openssl.org/source/openssl-1.1.1l.tar.gz

# 編譯安裝
mkdir /usr/local/ssl
./config --prefix=/usr/local/ssl --openssldir=/usr/local/ssl no-zlib
make && make install
echo "/usr/local/ssl/lib" >> /etc/ld.so.conf

測試是否安裝好,ldconfig -v 顯示出相關內容,並檢視/usr/local/ssl目錄下確實有安裝好的檔案

# 回到Python源嗎目錄下
make clean

# 設定安裝位置
./configure prefix=/usr/local/python3

修改Modules/Setup檔案

make && make install

ln -s /usr/local/python3/bin/python3 /usr/local/bin/python3
ln -s /usr/local/python3/bin/pip3 /usr/local/bin/pip3

 安裝QT5

1、下載安裝包:https://download.qt.io/archive/qt/5.12/5.12.12/

2、安裝依賴包:yum -y install mesa-libGL-devel mesa-libGLU-devel freeglut-devel gdb

3、安裝包授權:chmod +x qt-opensource-linux-x64-5.12.12.run

4、使用mstsc遠端登入CentOS桌面,執行./qt-opensource-linux-x64-5.12.12.run通過圖形化介面安裝,安裝時會提示註冊按提示資訊操作即可

檔案轉換服務

import traceback, uuid, os
from flask import Flask, request, send_from_directory
from pywpsrpc.rpcwpsapi import (createWpsRpcInstance, wpsapi)
from pywpsrpc.common import (S_OK, QtApp)


app = Flask(__name__)
# 設定檔案上傳儲存路徑
app.config['UPLOAD_FOLDER'] = '/tmp'
# MAX_CONTENT_LENGTH設定上傳檔案的大小,單位位元組
app.config['MAX_CONTENT_LENGTH'] = 1024 * 1024 * 1024


formats = {
    "doc": wpsapi.wdFormatDocument,
    "docx": wpsapi.wdFormatXMLDocument,
    "rtf": wpsapi.wdFormatRTF,
    "html": wpsapi.wdFormatHTML,
    "pdf": wpsapi.wdFormatPDF,
    "xml": wpsapi.wdFormatXML,
}


class ConvertException(Exception):

    def __init__(self, text, hr):
        self.text = text
        self.hr = hr

    def __str__(self):
        return """Convert failed:
Details: {}
ErrCode: {}
""".format(self.text, hex(self.hr & 0xFFFFFFFF))


def convert_to(path, format, abort_on_fails=False):
    hr, rpc = createWpsRpcInstance()
    if hr != S_OK:
        raise ConvertException("Can't create the rpc instance", hr)

    hr, app = rpc.getWpsApplication()
    if hr != S_OK:
        raise ConvertException("Can't get the application", hr)

    try:
        # we don't need the gui
        app.Visible = False

        docs = app.Documents

        def _handle_result(hr):
            if abort_on_fails and hr != S_OK:
                raise ConvertException("convert_file failed", hr)

        hr = convert_file(path, docs, format)
        _handle_result(hr)
    except Exception as e:
        print(traceback.format_exc())

    app.Quit(wpsapi.wdDoNotSaveChanges)


def convert_file(file, docs, format):
    hr, doc = docs.Open(file, ReadOnly=True)
    if hr != S_OK:
        return hr

    out_dir = os.path.dirname(os.path.realpath(file)) + "/out"
    os.makedirs(out_dir, exist_ok=True)

    # you have to handle if the new_file already exists
    new_file = out_dir + "/" + os.path.splitext(os.path.basename(file))[0] + "." + format
    ret = doc.SaveAs2(new_file, FileFormat=formats[format])

    # always close the doc
    doc.Close(wpsapi.wdDoNotSaveChanges)

    return ret


@app.route("/convert", methods=["POST"])
def do_convert():
    try:

        # 轉換格式引數
        convert_format = request.form['format']

        # 獲取檔案引數
        file = request.files['file']
        # 檔案字尾
        file_ext = file.filename.rsplit('.')[-1]
        file_name_uuid = str(uuid.uuid4())
        # UUID檔名
        file_name = file_name_uuid + "." + file_ext
        # 儲存檔案
        file.save(os.path.join(app.config['UPLOAD_FOLDER'], file_name))

        # 檔案轉換
        print("開始轉換檔案[%s]" % file_name)
        convert_to(os.path.join(app.config['UPLOAD_FOLDER'], file_name), convert_format, True)
        print("檔案轉換完成[%s]" % file_name)

        # 返回轉換後的檔案
        convert_file_name = file_name_uuid + "." + convert_format
        return send_from_directory(app.config['UPLOAD_FOLDER'] + '/out', convert_file_name, as_attachment=True)

    except Exception as e:
        print('檔案轉換時發生錯誤')
        print(traceback.format_exc())
        return {"success": False}


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=9000)

使用mstsc遠端登入CentOS桌面,執行啟動指令碼:

#!/bin/bash
set -e

export PATH=$PATH:/opt/kingsoft/wps-office/office6
export LD_LIBRARY_PATH=/opt/Qt5.12.12/5.12.12/gcc_64/lib
python3 /root/converter.py

服務驗證

1、遠端桌面登入CentOS桌面後能夠正常開啟WPS軟體,沒有提示字型依賴錯誤;

2、檔案轉換介面測試:

curl --location --request POST 'http://10.32.x.x:9000/convert' \
--header 'User-Agent: Apifox/1.0.0 (https://apifox.com)' \
--header 'Accept: */*' \
--header 'Host: 10.32.x.x:9000' \
--header 'Content-Type: multipart/form-data; boundary=--------------------------851319197095122366611715' \
--form 'format="pdf"' \
--form 'file=@"需要轉換的DOC檔案路徑"'