手把手教你用 Spring Boot搭建一個(gè)在線(xiàn)文件預(yù)覽系統(tǒng)拾因!支持ppt、doc等多種類(lèi)型文件預(yù)覽

昨晚搭建環(huán)境都花了好一會(huì)時(shí)間旷余,主要在浪費(fèi)在了安裝 openoffice 這個(gè)依賴(lài)環(huán)境上(Mac 需要手動(dòng)安裝)绢记。

然后,又一步一步功能演示正卧,記錄蠢熄,調(diào)試項(xiàng)目,并且簡(jiǎn)單研究了一下核心代碼之后才把這篇文章寫(xiě)完炉旷。

另外签孔,這篇文章我還會(huì)簡(jiǎn)單分析一下項(xiàng)目核心代碼叉讥。

如果有幫助,歡迎點(diǎn)贊/再看鼓勵(lì)饥追,我會(huì)開(kāi)心很久 ?( ′???` )比心

項(xiàng)目介紹

官方是這樣介紹 kkFileView 的:

kkFileView 是使用 spring boot 打造文件文檔在線(xiàn)預(yù)覽項(xiàng)目解決方案图仓,支持 doc、docx但绕、ppt救崔、pptx、xls捏顺、xlsx六孵、zip、rar草丧、mp4狸臣、mp3 以及眾多類(lèi)文本如 txt、html昌执、xml烛亦、java、properties懂拾、sql煤禽、js、md岖赋、json檬果、conf、ini唐断、vue选脊、php、py脸甘、bat恳啥、gitignore 等文件在線(xiàn)預(yù)覽

簡(jiǎn)單來(lái)說(shuō) kkFileView 就是常見(jiàn)的文件類(lèi)型的在線(xiàn)預(yù)覽解決方案。

總的來(lái)說(shuō)我覺(jué)得 kkFileView 是一個(gè)非常棒的開(kāi)源項(xiàng)目丹诀,在線(xiàn)文件預(yù)覽這個(gè)需求非常常見(jiàn)钝的。感謝開(kāi)源!

下面铆遭, 我站在一個(gè)“上帝”的角度從多個(gè)維度來(lái)評(píng)價(jià)一下 kkFileView:

  1. 代碼質(zhì)量一般硝桩,有很多可以?xún)?yōu)化的地方比如:
    • Controller 層代碼嵌套太多邏輯
    • 沒(méi)有進(jìn)行全局異常處理(代碼中是直接返回錯(cuò)誤信息的 json 數(shù)據(jù)給前端,我并不推薦這樣做)
    • 返回值不需要通過(guò)ObjectMapper轉(zhuǎn)換為 JSON 格式(ResponseEntity+@RestController 就行了)
    • ......
  2. 使用的公司比較多枚荣,說(shuō)明項(xiàng)目整體功能還是比較穩(wěn)定和成熟的碗脊!
  3. 代碼整體邏輯還是比較清晰的,比較容易看懂棍弄,給作者們點(diǎn)個(gè)贊望薄!

環(huán)境搭建

克隆項(xiàng)目

通過(guò)以下命令即可將項(xiàng)目克隆到本地:

git clone https://gitee.com/kekingcn/file-online-preview.git

安裝 OpenOffice

office 類(lèi)型的文件的預(yù)覽依賴(lài)了 OpenOffice 疟游,所以我們首先要安裝 OpenOffice(Windows 下已內(nèi)置,Linux 會(huì)自動(dòng)安裝痕支,Mac OS 下需要手動(dòng)安裝)颁虐。

下面演示一下如何在 Mac 上安裝 OpenOffice。

你可以通過(guò)以下命令安裝最新版的 OpenOffice:

brew cask install openoffice

不過(guò)卧须,這種方式下載可能會(huì)比較慢另绩,你可以直接去官網(wǎng)下載 dmg 安裝包。

官方下載地址:https://www.openoffice.org/download/

OpenOffice下載

很多小伙伴就要問(wèn)了:OpenOffice 是什么呢花嘶?

OpenOffice 是 Apache 旗下的一款開(kāi)源免費(fèi)的文字處理軟件笋籽,支持 Windows、Liunx椭员、OS X 等主流操作系統(tǒng)车海。

OpenOffice 和 Windows 下 office 辦公軟件有點(diǎn)類(lèi)似,不過(guò)其實(shí)開(kāi)源免費(fèi)的隘击。

why openoffice

啟動(dòng)項(xiàng)目

運(yùn)行FilePreviewApplication的 main 方法侍芝,服務(wù)啟動(dòng)后,訪(fǎng)問(wèn)http://localhost:8012/ 會(huì)看到如下界面埋同,代表服務(wù)啟動(dòng)成功州叠。

項(xiàng)目啟動(dòng)成功

使用

我們首先上傳了 3 個(gè)不同的類(lèi)型的文件來(lái)分別演示一下圖片、PDF凶赁、Word 文檔的預(yù)覽咧栗。

圖片的預(yù)覽

kkFileView 支持 jpg,jpeg虱肄,png致板,gif 等多種格式圖片的預(yù)覽,還包括了翻轉(zhuǎn)咏窿,縮放圖片等操作可岂。

圖片的預(yù)覽效果如下。

圖片的預(yù)覽效果

Word 文檔的預(yù)覽

kkFileView 支持 doc翰灾,docx 文檔預(yù)覽。

另外稚茅,根據(jù) Word 大小以及網(wǎng)速問(wèn)題纸淮, Word 預(yù)覽提供了兩種模式:

  • 每頁(yè) Word 轉(zhuǎn)為圖片預(yù)覽
  • 整個(gè) Word 文檔轉(zhuǎn)成 PDF,再預(yù)覽 PDF亚享。

兩種模式的適用場(chǎng)景如下

  • 圖片預(yù)覽 :Word 文件大(加載 PDF 速度比較慢)的情況咽块。
  • PDF 預(yù)覽 :內(nèi)網(wǎng)訪(fǎng)問(wèn)(加載 PDF 速度比較快)的情況。

圖片預(yù)覽模式預(yù)覽效果如下:

PDF 預(yù)覽模式預(yù)覽效果如下:

PDF 文檔的預(yù)覽

kkFileView 支持 PDF 文檔預(yù)覽欺税。類(lèi)似 Word 文檔預(yù)覽侈沪, PDF 預(yù)覽提供了兩種模式:

  • 每頁(yè) Word 轉(zhuǎn)為圖片預(yù)覽
  • 整個(gè) Word 文檔轉(zhuǎn)成 PDF揭璃,再預(yù)覽 PDF。

由于和 Word 文檔的預(yù)覽展示效果一致亭罪,這里就不放圖片了瘦馍。

文件預(yù)覽核心代碼分析

API 層

文件預(yù)覽調(diào)用的接口是 /onlinePreview

通過(guò)分析 /onlinePreview 接口我們發(fā)現(xiàn)应役, 后端接收到預(yù)覽請(qǐng)求之后情组,會(huì)從 URL 和請(qǐng)求中篩選出自己需要的信息比如文件后綴、文件名箩祥。

之后會(huì)調(diào)用FilePreview類(lèi) 的 filePreviewHandle() 方法院崇。filePreviewHandle() 方法是實(shí)現(xiàn)文件預(yù)覽的核心方法。

@RequestMapping(value = "/onlinePreview")
public String onlinePreview(String url, Model model, HttpServletRequest req) {
    FileAttribute fileAttribute = fileUtils.getFileAttribute(url);
    req.setAttribute("fileKey", req.getParameter("fileKey"));
    model.addAttribute("pdfDownloadDisable", ConfigConstants.getPdfDownloadDisable());
    model.addAttribute("officePreviewType", req.getParameter("officePreviewType"));
    FilePreview filePreview = previewFactory.get(fileAttribute);
    logger.info("預(yù)覽文件url:{}袍祖,previewType:{}", url, fileAttribute.getType());
    return filePreview.filePreviewHandle(url, model, fileAttribute);
}

FilePreview 是文件預(yù)覽接口底瓣,不同的文件類(lèi)型的預(yù)覽都實(shí)現(xiàn)了 FilePreview 接口,并實(shí)現(xiàn)了 filePreviewHandle() 方法蕉陋。

文件預(yù)覽接口

public interface FilePreview {
    String filePreviewHandle(String url, Model model, FileAttribute fileAttribute);
}

不同的文件類(lèi)型的預(yù)覽都實(shí)現(xiàn)了 FilePreview 接口捐凭,如下圖所示。

不同文件類(lèi)型的預(yù)覽都會(huì)實(shí)現(xiàn) FilePreview 接口寺滚,然后重寫(xiě)filePreviewHandle()方法柑营。比如: OfficeFilePreviewImpl 這個(gè)主要負(fù)責(zé)處理 office 文件的預(yù)覽、PdfFilePreviewImpl 主要負(fù)責(zé)處理 pdf 文件的預(yù)覽村视。

文件預(yù)覽具體實(shí)現(xiàn)分析

下面我們以 office 文件的預(yù)覽為入口來(lái)分析官套。

首先要明確的是 excel 類(lèi)型的預(yù)覽是通過(guò)將 excel 文件轉(zhuǎn)換為 HTML 實(shí)現(xiàn)的,其他類(lèi)型 office 文件的預(yù)覽是通過(guò)將文件轉(zhuǎn)換為 PDF 或者是 圖片的方式來(lái)預(yù)覽的蚁孔。

舉個(gè)例子奶赔。我們上傳了一份名為 武漢市文化市場(chǎng)管理辦法.docx 的 Word 文件并預(yù)覽的話(huà),jodconverter-web/src/main/file 路徑下會(huì)生成兩個(gè)相關(guān)文件杠氢,這兩個(gè)文件分別對(duì)應(yīng)了我們提到的 PDF 預(yù)覽 和 圖片預(yù)覽這兩種方式站刑。

  • 武漢市文化市場(chǎng)管理辦法.pdf
  • 由 Word 文件所轉(zhuǎn)化得到的一系列圖片

我們以一個(gè)名為 武漢市文化市場(chǎng)管理辦法.docx 的文件來(lái)舉例說(shuō)明一下代碼中是如何做的。

通過(guò)分析代碼鼻百, 我們定位到了 OfficeFilePreviewImpl 這個(gè)主要負(fù)責(zé)處理 office 文件預(yù)覽的類(lèi)绞旅。

/**
 * 處理office文件
 */
@Service
public class OfficeFilePreviewImpl implements FilePreview {

}

我們來(lái)簡(jiǎn)單分析一下 OfficeFilePreviewImpl 類(lèi)中實(shí)現(xiàn)預(yù)覽的核心方法是 filePreviewHandle

說(shuō)明:這部分代碼的邏輯不夠清晰温艇,還可以抽方法優(yōu)化以讓人更容易讀懂因悲,感興趣的小伙伴可以自己動(dòng)手重構(gòu)一下,然后去給作者提個(gè) PR勺爱。

    @Override
    public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
        // 1.獲取預(yù)覽類(lèi)型(image/pdf/html)晃琳,用戶(hù)請(qǐng)求中傳了officePreviewType參數(shù)就取參數(shù)的,沒(méi)傳取系統(tǒng)默認(rèn)(image)
        String officePreviewType = model.asMap().get("officePreviewType") == null ? ConfigConstants.getOfficePreviewType() : model.asMap().get("officePreviewType").toString();
        // 2.獲取 URL 地址
        String baseUrl = BaseUrlFilter.getBaseUrl();// http://localhost:8012/
        // 3.獲取圖片相關(guān)信息
        String suffix=fileAttribute.getSuffix();//文件后綴如docx
        String fileName=fileAttribute.getName();//文件名如:武漢市文化市場(chǎng)管理辦法.docx
        // 4. 判斷是否為 html 格式預(yù)覽也就是判斷文件否為 excel
        boolean isHtml = suffix.equalsIgnoreCase("xls") || suffix.equalsIgnoreCase("xlsx");
        // 5. 將文件的后綴名更換為 .pdf 或者 .html(excel文件的情況)
        String pdfName = fileName.substring(0, fileName.lastIndexOf(".") + 1) + (isHtml ? "html" : "pdf");
        // 6. 轉(zhuǎn)換后的文件輸出的文件夾如 file-online-preview/jodconverter-web/src/main/file/武漢市文化市場(chǎng)管理辦法.pdf)
        String outFilePath = FILE_DIR + pdfName;
        // 7 .判斷之前是否已轉(zhuǎn)換過(guò),如果轉(zhuǎn)換過(guò)卫旱,直接返回人灼,否則執(zhí)行轉(zhuǎn)換
        // 文件第一次被預(yù)覽的時(shí)候會(huì)首先對(duì)文件進(jìn)行緩存處理
        if (!fileUtils.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) {
            String filePath;
            // 下載文件
            ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, null);
            if (0 != response.getCode()) {
                model.addAttribute("fileType", suffix);
                model.addAttribute("msg", response.getMsg());
                return "fileNotSupported";
            }
            filePath = response.getContent();
            if (StringUtils.hasText(outFilePath)) {
                officeToPdf.openOfficeToPDF(filePath, outFilePath);
                if (isHtml) {
                    // 對(duì)轉(zhuǎn)換后的文件進(jìn)行操作(改變編碼方式)
                    fileUtils.doActionConvertedFile(outFilePath);
                }
                if (ConfigConstants.isCacheEnabled()) {
                    // 加入緩存
                    fileUtils.addConvertedFile(pdfName, fileUtils.getRelativePath(outFilePath));
                }
            }
        }
        // 8.根據(jù)預(yù)覽類(lèi)型officePreviewType,選擇不同的預(yù)覽方式
        // 比如,如果預(yù)覽類(lèi)型officePreviewType為pdf則進(jìn)行pdf方式預(yù)覽
        if (!isHtml && baseUrl != null && (OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) || OFFICE_PREVIEW_TYPE_ALL_IMAGES.equals(officePreviewType))) {
            return getPreviewType(model, fileAttribute, officePreviewType, baseUrl, pdfName, outFilePath, pdfUtils, OFFICE_PREVIEW_TYPE_IMAGE);
        }
        model.addAttribute("pdfUrl", pdfName);
        return isHtml ? "html" : "pdf";
    }

項(xiàng)目地址: https://github.com/kekingcn/kkFileView

我整理了一份優(yōu)質(zhì)原創(chuàng)PDF資源免費(fèi)分享給大家顾翼,大部分內(nèi)容都是我的原創(chuàng)投放,少部分來(lái)自朋友。

<img src="https://cdn.jsdelivr.net/gh/javaguide-tech/blog-images/2020-10/image-20201012105544846.png" style="zoom:50%;" />

<img src="https://cdn.jsdelivr.net/gh/javaguide-tech/blog-images/2020-10/image-20201012105608336.png" alt="image-20201012105608336" style="zoom:50%;" />

下載地址:https://cowtransfer.com/s/fbed14f0c22a4d 暴构。

我的開(kāi)源項(xiàng)目推薦

  1. JavaGuide :「Java學(xué)習(xí)+面試指南」一份涵蓋大部分Java程序員所需要掌握的核心知識(shí)跪呈。準(zhǔn)備 Java 面試,首選 JavaGuide取逾!
  2. guide-rpc-framework :A custom RPC framework implemented by Netty+Kyro+Zookeeper.(一款基于 Netty+Kyro+Zookeeper 實(shí)現(xiàn)的自定義 RPC 框架-附詳細(xì)實(shí)現(xiàn)過(guò)程和相關(guān)教程)
  3. jsoncat :仿 Spring Boot 但不同于 Spring Boot 的一個(gè)輕量級(jí)的 HTTP 框架
  4. programmer-advancement :程序員應(yīng)該有的一些好習(xí)慣+面試必知事項(xiàng)耗绿!
  5. springboot-guide :Not only Spring Boot but also important knowledge of Spring(不只是SpringBoot還有Spring重要知識(shí)點(diǎn))
  6. awesome-java :Collection of awesome Java project on Github(Github 上非常棒的 Java 開(kāi)源項(xiàng)目集合).

作者介紹: Github 70k Star 項(xiàng)目 JavaGuide(公眾號(hào)同名) 作者。每周都會(huì)在公眾號(hào)更新一些自己原創(chuàng)干貨砾隅。公眾hao后臺(tái)回復(fù)“1”領(lǐng)取Java工程師必備學(xué)習(xí)資料+面試突擊pdf误阻。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市晴埂,隨后出現(xiàn)的幾起案子究反,更是在濱河造成了極大的恐慌,老刑警劉巖儒洛,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件精耐,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡琅锻,警方通過(guò)查閱死者的電腦和手機(jī)卦停,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)恼蓬,“玉大人惊完,你說(shuō)我怎么就攤上這事〈τ玻” “怎么了小槐?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)荷辕。 經(jīng)常有香客問(wèn)我凿跳,道長(zhǎng),這世上最難降的妖魔是什么疮方? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任拄显,我火速辦了婚禮,結(jié)果婚禮上案站,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好蟆盐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布承边。 她就那樣靜靜地躺著,像睡著了一般石挂。 火紅的嫁衣襯著肌膚如雪博助。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天痹愚,我揣著相機(jī)與錄音富岳,去河邊找鬼。 笑死拯腮,一個(gè)胖子當(dāng)著我的面吹牛窖式,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播动壤,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼萝喘,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了琼懊?” 一聲冷哼從身側(cè)響起阁簸,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎哼丈,沒(méi)想到半個(gè)月后启妹,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年六水,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了饵史。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡咙崎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出吨拍,到底是詐尸還是另有隱情褪猛,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布羹饰,位于F島的核電站伊滋,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏队秩。R本人自食惡果不足惜笑旺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望馍资。 院中可真熱鬧筒主,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至藤韵,卻和暖如春虐沥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背泽艘。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工欲险, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人匹涮。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓天试,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親焕盟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子秋秤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354