天天看點

SpringBoot內建onlyoffice實作word文檔編輯儲存說明Linux安裝Windows安裝添加字型對接業務系統

說明

onlyoffice為一款開源的office線上編輯元件,提供word/excel/ppt編輯儲存操作

  • 以下操作均基于centos8系統,officeonly鏡像版本7.1.2.23
  • 鏡像下載下傳位址:https://yunpan.360.cn/surl_y87CKKcPdY4 (提取碼:1f92)
  • 已解除20連接配接限制
  • 已導入常用中文字型,修改了字号
  • 已取消上傳檔案大小的限制,修改為500M
  • 隐藏所有操作按鈕,隐藏onlyoffice圖示和名稱,隻保基礎操作欄目
  • 僅用于word檔案和excel檔案編輯/儲存/檢視

Linux安裝

yum設定

  • 進入yum的repos目錄 cd /etc/yum.repos.d/
cd /etc/yum.repos.d/
           
  • 修改所有的CentOS檔案内容
sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*

sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
           
  • 更新yum源為阿裡鏡像
wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-vault-8.5.2111.repo

yum clean all

yum makecache
           
  • yum安裝測試是否可以yum安裝
yum install wget –y
           
  • 安裝依賴包
sudo yum install -y yum-utils device-mapper-persistent-data lvm2 
           

docker安裝

  • 設定鏡像源
sudo yum-config-manager --add-repo
https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
           
  • 安裝docker-ce社群版
sudo yum install docker-ce --allowerasing
           
  • 建立使用者組
建立 Docker 使用者組
sudo groupadd docker
添加目前使用者到 docker 組
sudo usermod -aG docker $USER
啟動docker
systemctl start docker.service
服務開機啟動
systemctl enable docker.service
           
  • 安裝docker圖形化管理頁面
docker volume create portainer_data

docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer
           

通路http://localhost:9000/進行管理端初始化設定

onlyoffice部署

  • 上傳鏡像檔案到伺服器
  • 載入鏡像
docker load < onlyoffice.tar
           
  • 檢視鏡像
docker images
           
  • 啟動容器
sudo docker run --name=onlyoffice -i -t -d -p  8088:80 --restart=always 鏡像id
           

Windows安裝

  • 安裝VMWare虛拟機,安裝centos8系統,參照上述步驟
  • 網絡配置成NAT模式,虛拟機進入centos系統後配置固定ip
  • NAT網卡設定端口轉發,轉發9000和8088端口,主機端口和虛拟端口一緻

添加字型

  • 找到對應字型,如果是windows下的字型進入C:\Windows\Fonts 搜尋
  • 安裝High-Logic FontCreator 運作Keygen.exe 點選generate擷取激活碼
  • 工具下載下傳:https://yunpan.360.cn/surl_y87CK8RNr8N (提取碼:7059)
  • 打開字型 font=>properties
  • 修改 font family 為custom 中對應的中文 ,導出字型
  • 上傳修改後的字型到liunx檔案下 eg:/home/fonts/
  • 檢視OnlyOffice容器id
docker ps
           
  • 進入OnlyOffice容器
docker exec -it 容器id /bin/bash
           
  • 删除字型緩存
cd /var/www/onlyoffice/documentserver/fonts

rm -rf *
           
  • 複制字型到 /var/www/onlyoffice/documentserver/core-fonts 下
docker cp /home/fonts onlyoffice:/var/www/onlyoffice/documentserver/core-fonts
           
  • 進入onlyoffice容器執行重新整理
/usr/bin/documentserver-generate-allfonts.sh
           
  • 浏覽器清除緩存重新重新整理

對接業務系統

onlyfiice部署完畢後會有一個服務位址,例如 http://localhost:8088/

引入api.js

不能下載下傳檔案本地引用,一定要線上引用

<script type="text/javascript" src="http://localhost:8088/web-apps/apps/api/documents/api.js"></script>
           

聲明dom

頁面添加一個div,用于渲染編輯器

<div id="placeholder" class="nav" style="width: 100%; height: 100%"></div>
           

頁面渲染

參數說明

key:對應文檔的一個辨別,建議前端随機生成,防止重複

url:打開文檔的位址,傳回流資料

fileType:文檔類型,例如:doc/docx

title:檔案名稱,例如:2022年工作方案.docx

model:打開模式,例如:edit(編輯模式)/view(閱讀模式)

callbackUrl:檔案修改後儲存回調位址

function initDoc(key, url, fileType, title, model, callbackUrl) {
                let config = {
                    "document": {
                        "documentType": "text",
                        "width": "100%", //打開視窗寬度
                        "height": "100%", //打開視窗高度
                        "fileType": fileType, //文檔類型
                        "key": key, //定義用于服務識别文檔的唯一文檔辨別符。每次編輯和儲存文檔時,都必須重新生成密鑰。長度限制為128個符号。
                        "title": title, //為檢視或編輯的文檔定義所需的檔案名,該檔案名也将在下載下傳文檔時用作檔案名。長度限制為128個符号。
                        "url": url, //定義存儲原始檢視或編輯的文檔的絕對URL
                        "info": {
                            "owner": "王重陽", //檔案建立者名稱
                            "sharingSettings": [ //檔案對應使用者的操作權限配置
                                {
                                    "permissions": "Full Access", // 完全操作權限-Full Access,隻讀權限-Read Only 拒絕通路-Deny Access
                                    "user": "林朝英" //有次權限的使用者
                                },
                                {
                                    "permissions": "Read Only",
                                    "user": "周伯通"
                                },
                            ],
                            "uploaded": "2010-07-07 3:46 PM" //檔案建立時間
                        },
                        //文檔權限參數
                        "permissions": {
                            "edit": true, //(檔案是否可以編輯,false時檔案不可編輯)
                            "fillForms": true, //定義是否能在文檔中填充表單
                            "print": true, //定義文檔是否能列印
                            "review": false, //第一是否顯示審閱文檔菜單
                            "comment": true, //定義是否可以注釋文檔。如果注釋權限設定為“ true”,則文檔側欄将包含“注釋”菜單選項;隻有将mode參數設定為edit時才生效,預設值與edit參數的值一緻。
                            "copy": true, //是否允許您将内容複制到剪貼闆。預設值為true。
                            "download": true, //定義是否可以下載下傳文檔或僅線上檢視或編輯文檔。如果下載下傳權限設定為“false”下載下傳為菜單選項将沒有。預設值為true。
                            "modifyContentControl": true, //定義是否可以更改内容控件設定。僅當mode參數設定為edit時,内容控件修改才可用于文檔編輯器。預設值為true。
                            "modifyFilter": true, //定義過濾器是否可以全局應用(true)影響所有其他使用者,或局部應用(false),即僅适用于目前使用者。如果将mode參數設定為edit,則過濾器修改僅對電子表格編輯器可用。預設值為true。
                        }
                    },
                    // type: "embedded",
                    //打開文檔類型
                    // text對應各種文檔類型(.doc, .docm, .docx, .dot, .dotm, .dotx, .epub, .fodt, .htm, .html, .mht, .odt, .ott, .pdf, .rtf, .txt, .djvu, .xps)
                    //spreadsheet對應表格類型(.csv, .fods, .ods, .ots, .xls, .xlsm, .xlsx, .xlt, .xltm, .xltx)
                    //presentation對應PPT類型(.fodp, .odp, .otp, .pot, .potm, .potx, .pps, .ppsm, .ppsx, .ppt, .pptm, .pptx)
                    "editorConfig": { //編輯配置
                        "createUrl": "http://docServer:port/url-to-create-document/", //指定建立檔案的頁面,添加該配置後文檔伺服器插件才會顯示建立檔案按鈕
                        "mode": model, //文檔操作模式 view 視圖模式不可編輯  edit 編輯模式可編輯文檔
                        "callbackUrl": callbackUrl, //儲存檔案時的回調位址
                        "lang": "zh-CN", //語言環境
                        "customization": { //定制部分允許自定義編輯器界面,使其看起來像您的其他産品,并更改是否存在其他按鈕,連結,更改徽标和編輯者所有者詳細資訊。
                            "help": false, //定義是顯示還是隐藏“幫助”菜單按鈕。預設值為true。
                            "hideRightMenu": false, //定義在第一次加載時是顯示還是隐藏右側菜單。預設值為false。
                            "autosave": false, //定義是啟用還是禁用“自動儲存”菜單選項。請注意,如果您在菜單中更改此選項,它将被儲存到浏覽器的localStorage中。預設值為true。
                            "forcesave": true, //定義儲存按鈕是否顯示預設false
                            "chat": false, //定義“聊天”菜單按鈕是顯示還是隐藏;請注意,如果您隐藏“聊天”按鈕,則相應的聊天功能也将被禁用。預設值為true。
                            "commentAuthorOnly": false, //定義使用者是否隻能編輯和删除他的評論。預設值為false。
                            "comments": false, //定義是顯示還是隐藏“注釋”菜單按鈕;請注意,如果您隐藏“評論”按鈕,則相應的評論功能将僅可用于檢視,評論的添加和編輯将不可用。預設值為true。
                            "compactHeader": false, //定義是否将菜單欄放在在徽标旁邊使界面更加緊湊預設false。
                            "compactToolbar": false, //定義顯示的頂部工具欄類型是完整(false)還是緊湊true。預設值為false 多餘菜單将在右側折疊點選顯示。
                            "compatibleFeatures": false, //定義僅與OOXML格式相容的功能的使用。例如,不要在整個文檔上使用注釋。預設值為false。
                            "macros": false, //定義是否将運作文檔宏以及可用的宏設定。預設值為true。
                            "macrosMode": "warn", //定義是否将運作文檔宏。可以采用以下值: disable -根本不運作;enable -自動運作所有宏;warn -警告宏并請求允許運作。預設值為original。
                            "plugins": false, //定義是否将啟動插件并可用。預設值為true。
                            "showReviewChanges": false, //定義在加載編輯器時是否自動顯示或隐藏審閱更改面闆。預設值為false。
                            "spellcheck": false, //定義在加載編輯器時是否自動打開或關閉拼寫檢查器。拼寫檢查器僅适用于文檔編輯器和示範文稿編輯器。預設值為true。
                            "toolbarNoTabs": false, //定義是突出顯示頂部工具欄頁籤樣式。預設值為false。
                            "unit": "cm", //定義在标尺和對話框中使用的度量機關。可以采用以下值:cm -厘米,pt-點,inch -英寸。預設值為厘米(cm)。
                            "zoom": 100, //定義以百分比為機關的文檔顯示縮放值。可以取大于0的值。對于文本文檔和示範文稿,可以将此參數設定為-1(使文檔适合頁面選項)或-2(使文檔頁面寬度适合編輯器頁面)。預設值為100。
                            "customer": { //關于 文檔編輯器的顯示資訊
                                "address": "My City, 123a-45", //有權通路編輯或編輯作者的公司或個人的郵政位址,
                                "info": "Some additional information", //有關您希望其他人認識的公司或個人的一些其他資訊,
                                "logo": "https://example.com/logo-big.png", //圖檔徽标的路徑(此檔案沒有特别建議,但是如果使用透明背景的.png格式會更好)。圖檔必須具有以下尺寸:432x70,
                                "mail": "[email protected]", //有權通路編輯者或編輯者的公司或個人的電子郵件位址
                                "name": "歐陽鋒", //該公司或個人的誰可以通路編輯或編輯作者,名稱
                                "www": "example.com" //以上公司或個人的家庭網站位址,
                            },
                            "feedback": { //回報配置資訊
                                "url": "https://example.com", //單擊“回報和支援”菜單按鈕時将打開的網站位址的絕對URL ,
                                "visible": false //顯示或隐藏“回報和支援”菜單按鈕,
                            },
                            "goback": { //定義“打開檔案位置”菜單按鈕和右上角按鈕的設定。該對象具有以下參數:
                                "blank": true, //在新的浏覽器頁籤/視窗(如果值設定為true)或目前頁籤(如果值設定為false)中打開網站。預設值為true,
                                "requestClose": false, //定義如果單擊“打開檔案位置”按鈕,則調用events.onRequestClose事件,而不是打開浏覽器頁籤或視窗。預設值為false,
                                "text": "Open file location", //将在“打開檔案位置”菜單按鈕和右上角按鈕(即,而不是“轉到文檔”)上顯示的文本,
                                "url": "https://example.com" //單擊“打開檔案位置”菜單按鈕時将打開的網站位址的絕對URL ,
                            },
                            "logo": {
                                "image": "https://example.com/logo.png", //圖像檔案的路徑,用于在普通工作模式下顯示(即,在所有編輯器的檢視和編輯模式下)。圖檔必須具有以下尺寸:172x40,
                                "imageEmbedded": "https://example.com/logo_em.png", //用于以嵌入式模式顯示的圖像檔案的路徑(請參閱config部分以了解如何定義嵌入式文檔類型)。圖檔必須具有以下尺寸:248x40,
                                "url": "https://www.baidu.com" //某人單擊徽标圖像時将使用的絕對URL(可用于轉到您的網站等)。保留為空字元串或null以使徽标不可單擊,
                            },
                        },
                        "user": { //使用者資訊
                            "id": "admin", //使用者ID
                            "name": "操作員" //使用者全名稱
                        },
                        "embedded": { //Embedded部分僅适用于嵌入式文檔類型(請參閱config部分以了解如何定義嵌入式文檔類型)。它允許更改設定,這些設定定義嵌入式模式下按鈕的行為。
                            "embedUrl": "https://example.com/embedded?doc=exampledocument1.docx", //定義文檔的絕對URL,以作為嵌入到網頁中的文檔的源檔案
                            "fullscreenUrl": "https://example.com/embedded?doc=exampledocument1.docx#fullscreen", //定義将以全屏模式打開的文檔的絕對URL。
                            "saveUrl": "https://example.com/download?doc=exampledocument1.docx", //定義允許将文檔儲存到使用者個人計算機上的絕對URL。
                            "shareUrl": "https://example.com/view?doc=exampledocument1.docx", //定義允許其他使用者共享此文檔的絕對URL。
                            "toolbarDocked": "top" //定義嵌入式檢視器工具欄的位置,可以為top或bottom。
                        }
                    },

                    "events": { //事件配置
                        // onAppReady,//-将應用程式加載到浏覽器時調用的函數。
                        // onCollaborativeChanges //-當文檔由其他使用者在嚴格共同編輯模式下共同編輯時調用的函數。
                        // onDocumentReady,//-将應用程式加載到浏覽器時調用的函數。
                        // onDocumentStateChange,//-修改文檔時調用的函數。這就是所謂的使用參數:{真正的“資料”}在目前使用者編輯文檔以及與參數:{“資料”:假}在目前使用者的更改發送到文檔編輯服務。
                        // onDownloadAs,//-調用downloadAs方法時,使用指向已編輯檔案的絕對URL調用的函數。在data參數中發送要下載下傳的文檔的絕對URL 。
                        // onError,//-發生錯誤或其他特定事件時調用的函數。錯誤消息在data參數中發送。
                        // onInfo,//-應用程式打開檔案時調用的函數。該模式在data.mode參數中發送。可以檢視或編輯。
                        // onMetaChange,//-通過meta指令更改文檔的元資訊時調用的函數。文檔名稱通過data.title參數發送。
                        // onOutdatedVersion,//-使用舊的document.key值打開文檔進行編輯時,顯示錯誤後調用的函數,該值用于編輯先前的文檔版本并已成功儲存。調用此事件時,必須使用新的document.key重新初始化編輯器。
                        // onReady,//-将應用程式加載到浏覽器時調用的函數。自從5.0版本不推薦使用,請使用onAppReady代替
                        // onRequestClose,//-結束編輯器的工作并且必須關閉編輯器時調用的函數。
                        // onRequestCompareFile,//-使用者嘗試通過單擊“存儲中的文檔”按鈕來選擇要比較的文檔時調用的函數。要選擇要比較的文檔,必須調用setRevisedFile方法。如果未聲明該方法,則不會顯示“來自存儲的文檔”按鈕。
                        // onRequestCreateNew,//-使用者嘗試通過單擊“建立”按鈕來建立文檔時調用的函數。使用此方法代替createUrl字段。如果未聲明該方法且未指定createUrl,則将不會顯示“建立新”按鈕。
                        // onRequestEditRights,//-使用者嘗試通過單擊“編輯文檔”按鈕嘗試将文檔從視圖切換到編輯模式時調用的函數。調用該函數時,必須在編輯模式下再次初始化編輯器。如果未聲明該方法,則不會顯示“編輯”按鈕。
                        // onRequestHistory,//-使用者嘗試通過單擊“版本曆史記錄”按鈕顯示文檔版本曆史記錄時調用的函數。要顯示文檔版本曆史,您必須調用refreshHistory方法。如果未聲明該方法和onRequestHistoryData方法,則不會顯示“版本曆史記錄”按鈕。
                        // onRequestHistoryClose,//-當使用者嘗試通過單擊“關閉曆史記錄”按鈕來檢視文檔版本曆史記錄時,試圖調用該文檔時調用的函數。調用該函數時,必須在編輯模式下再次初始化編輯器。如果未聲明該方法,則不會顯示“關閉曆史記錄”按鈕。
                        // onRequestHistoryData,//-使用者嘗試單擊文檔版本曆史記錄中的特定文檔版本時調用的函數。
                        // onRequestInsertImage,//-使用者嘗試通過單擊“儲存圖像”按鈕插入圖像時調用的函數。圖像插入的類型在參數data.c中指定。
                        // onRequestRename,//-使用者嘗試通過單擊“重命名...”按鈕重命名檔案時調用的函數。
                        // onRequestRestore,//-使用者單擊版本曆史記錄中的“還原”按鈕來還原檔案版本時調用的函數。
                        // onRequestSaveAs,//-使用者嘗試通過單擊“另存為...”按鈕儲存檔案時調用的函數。文檔的标題和要下載下傳的文檔的絕對URL在data參數中發送。如果未聲明該方法,則不會顯示“另存為...”按鈕。
                        // onRequestSharingSettings,//-使用者單擊“更改通路權限”按鈕來管理文檔通路權限時調用的函數。必須調用setSharingSettings方法來更新有關允許與其他使用者共享文檔的設定的資訊。如果未聲明該方法,則不會顯示“更改通路權限”按鈕。
                        // onRequestUsers,//-評論者可以選擇要在評論中提及的其他使用者時調用的函數。要設定使用者清單,必須調用setUsers方法。
                        // onWarning,//-發生警告時調用的函數。警告消息在data參數中發送。
                        // "onDocumentStateChange": function() {
                        // }, //文檔改變後的回調
                        //"onDocumentReady" : onDocumentReady, //文檔初始化準備好後的回調
                    },
                };
                var docEditor = new DocsAPI.DocEditor("placeholder", config);
            }
           

資料接口

  • 下載下傳檔案

傳回資料流即可,示例如下

@GetMapping("/download")
    @ResponseBody
    public void download(@RequestParam("attguid") String attguid, HttpServletRequest request, HttpServletResponse response) throws Exception {
        AttachmentDO attachment = attachmentService.selectOne(attguid);
        String filePath = "";
        //雲上傳的附件
        if (attachment.getVirtualpath().contains("ReadAlOSS")) {
            if (attachment.getCanedit() == null || attachment.getCanedit() == 20) {
                String fileurl = aliUtil.readOSSFile(attachment);
                if (!StringUtil.isEmpty(fileurl)) {
                    response.sendRedirect(fileurl);
                }
            } else if (attachment.getCanedit() == 30) {
                String fileurl = huaWeiUtil.readOBSFile(attachment);
                System.out.println(fileurl);
                if (!StringUtil.isEmpty(fileurl)) {
                    response.sendRedirect(fileurl);
                }
            } else if (attachment.getCanedit() == 40) {
                String fileurl = minioUtil.readMinioFile(attachment);
                System.out.println(fileurl);
                if (!StringUtil.isEmpty(fileurl)) {
                    response.sendRedirect(fileurl);
                }
            }
        }
        //本地檔案
        String configPath = frameConfig.getAttachPath();
        filePath = configPath + attachment.getVirtualpath();

        File file = new File(filePath);
        if (file.exists()) {
            String filename = attachment.getFilename();
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "utf-8"));
            response.setCharacterEncoding("utf-8");
            response.setContentLength((int) file.length());

            byte[] buff = new byte[(int) file.length()];
            BufferedInputStream bufferedInputStream = null;
            OutputStream outputStream = null;
            try {
                outputStream = response.getOutputStream();
                bufferedInputStream = new BufferedInputStream(new FileInputStream(file));
                int i = 0;
                while ((i = bufferedInputStream.read(buff)) != -1) {
                    outputStream.write(buff, 0, i);
                    outputStream.flush();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {

                try {
                    bufferedInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
           
  • 儲存檔案

解析傳遞的參數,擷取檔案url下載下傳到本地後,進行自定義業務操作

@PostMapping("/save")
    @ResponseBody
    public void save(@RequestParam Map<String, String> map, HttpServletRequest request, HttpServletResponse response) {
        PrintWriter writer = null;
        String body = "";
        String attguid = request.getParameter("attguid");
        try {
            writer = response.getWriter();
            Scanner scanner = new Scanner(request.getInputStream());
            scanner.useDelimiter("\\A");
            body = scanner.hasNext() ? scanner.next() : "";
            scanner.close();
        } catch (Exception ex) {
            writer.write("get request.getInputStream error:" + ex.getMessage());
            return;
        }

        if (body.isEmpty()) {
            throw new CustomerRuntimeException("ONLYOFFICE回調儲存請求體未空");
        }

        JSONObject jsonObj = JSONObject.parseObject(body);
        int status = (Integer) jsonObj.get("status");
        int saved = 0;
        String key = jsonObj.get("key").toString();
        if (status == 2 || status == 3 || status == 6) //MustSave, Corrupted
        {
            String downloadUri = (String) jsonObj.get("url");
            System.out.println(downloadUri);
            try {
                String filePath = "tempfiles/onlyoffice/savedownload/";
                FileUtil.initfloderPath(filePath);
                String fileName = CommonUtil.getNewGuid();
                HttpUtil.downLoadFromUrl(downloadUri, filePath, fileName);
                attachLogic.updateAttachContent(attguid, FileUtil.getBytes(filePath + fileName));
            } catch (Exception ex) {
                saved = 1;
                ex.printStackTrace();
            }
        }
        writer.write("{\"error\":" + saved + "}");
    }
           

外部按鈕接入

以儲存按鈕為例

function HandleSave() {
        var frameDocument = document.getElementsByTagName("iframe")[0].contentDocument;
        frameDocument.getElementById("slot-btn-dt-save").getElementsByTagName("button")[0].click();
    }

    window.onmessage = function (event) {
        var data = JSON.parse(event.data);
        if (data.event == "onDocumentStateChange" && data.data == false) {
            OpenSuccessMessage("儲存成功")
        }
    }
           

繼續閱讀