Vue項目pdf(base64)轉(zhuǎn)圖片

公司有個業(yè)務需求,要求后臺傳pdf的base64編碼給前端,前端顯示到界面上,后來在網(wǎng)上搜索了很多關于base64轉(zhuǎn)pdf的文章,都寫的不是非常的詳細,在實現(xiàn)的過程中遇到很多坑,經(jīng)過一天的研究終于實現(xiàn)了這個功能,分享一下我在這個功能中遇到的問題和解決方法

要注明的是這里用到的核心插件是pdf.js,原理是動態(tài)生成canvas標簽,然后通過pdf.js生成一個能渲染出pdf的對象,隨后渲染每個canvas,并且生成的pdf是畫面的形式,并沒有pdf之類的控件

引入插件

這里很多博客都是使用JavaScript原生的方法引入pdf.js,例如使用script標簽引入外部的js腳本,或者直接把pdf.js的源碼復制到項目中,但是我嘗試這些方法的時候都不是特別好用,而且引入后導致項目體積過于龐大,


隨后我去github上尋找通過包管理器引入pdf.js的方法,在pdf.js的github上官方說明的是用gulp如何使用pdf.js,但是對于npm來說并沒有詳細說明,終于我在字里行間發(fā)現(xiàn)了這么一句話

To use PDF.js in a web application you can choose to use a pre-built version of the library or to build it from source. We supply pre-built versions for usage with NPM and Bower under the pdfjs-dist name. For more information and examples please refer to the wiki page on this subject.

大致的意思就是如果使用npm包管理器或者bower的話,引入構建好的版本的名字為pdfjs-dist,那么我們使用npm的方法引入這個pdfjs-dist,引入的名字就隨意取名了這里我叫PDFJS

 import PDFJS from 'pdfjs-dist'

使用pdfjs-dist

這里后臺傳給我的是一個由pdf文件名字和pdf的base64編碼組成的對象的數(shù)組,我取名為pdfDataList


可以看到fileName是pdf的名字,fileVale是pdf文件的base64編碼,thumbnail是pdf縮略圖的base64編碼這里用不到先不管,之前說到需要動態(tài)生成canvas節(jié)點(這里不會canvas也不要緊,只需要根據(jù)代碼一步步做就能渲染canvas)

  1. 首先我們創(chuàng)建一個承載所有canvas節(jié)點的父節(jié)點,取名為pdfList


  2. 然后創(chuàng)建一個異步函數(shù)showPdf(不懂什么是異步函數(shù)的可以去查一下async/await,這里不用異步函數(shù)也可以使用promise.then的方法,但是async/await作為異步操作的終極方案最好還是學習一下)

async showPdf() {
       
    }
  1. 使用querySelector選擇類名為pdfList的dom節(jié)點,隨后遍歷后臺傳過來的pdfDataList數(shù)組的每一項,這里用到一個瀏覽器自帶的atob()方法解碼base64,MDN上是這么解釋的:

你可以使用 window.btoa() 方法來編碼一個可能在傳輸過程中出現(xiàn)問題的數(shù)據(jù)谋右,并且在接受數(shù)據(jù)之后,使用 atob() 方法再將數(shù)據(jù)解碼衰齐。

語法: var decodedData = scope.atob(encodedData);

隨后調(diào)用pdf.js插件的getDocument方法,getDocument是一個promise,所以使用異步函數(shù)的話前面需要加await關鍵字(不使用異步函數(shù)的話在方法后面加.then((pdf)=>{.......}),這個pdf對象和我這個pdf對象是同一個,同時這里暫時也沒考慮異步操作出錯的情況,有要求的話可以在加個catch捕獲錯誤)
getDocument方法的參數(shù)是一個對象,對象鍵名為data,值為base64解碼后的值,此方法返回一個pdf對象,這個對象有幾個屬性,可以打印出來觀察一下

這里我們先用到的是numPages屬性,它指的是當前pdf文件有多少頁

async showPdf() {
        let pdfList = document.querySelector('.pdfList') //通過querySelector選擇DOM節(jié)點,使用document.getElementById()也一樣
        for(let value of this.pdfDataList){ //遍歷后臺傳過來的pdfDataList
            let base64 = value.fileValue //獲得bas464編碼
            let decodedBase64 = atob(base64) //使用瀏覽器自帶的方法解碼
            let pdf = await  PDFJS.getDocument({data: decodedBase64}) //返回一個pdf對象
            let pages = pdf.numPages //聲明一個pages變量等于當前pdf文件的頁數(shù)
        }
    }
  1. 獲取當前pdf文件的對象和當前pdf文件的所有頁數(shù)后,循環(huán)遍歷每個頁數(shù),執(zhí)行如下操作:

1)動態(tài)創(chuàng)建canvas節(jié)點

2)調(diào)用pdf對象原型上的getPage()方法和getViewport()方法,依次傳入當前循環(huán)的頁數(shù)和canvas的縮放大小(這里不懂的可以直接復制黏貼)

3)渲染當前的canvas節(jié)點

4)調(diào)用page對象的render()方法渲染當前頁,此方法也是一個promise,需要使用await關鍵字等到狀態(tài)為resolve后再執(zhí)行之后的代碼

5)給顯示當前頁面的canvas節(jié)點一個className為canvas方便修改樣式,最后把這個canvas節(jié)點插入到pdfList節(jié)點中

async showPdf() {
        let pdfList = document.querySelector('.pdfList') //通過querySelector選擇DOM節(jié)點,使用document.getElementById()也一樣
        for(let value of this.pdfDataList){ //遍歷后臺傳過來的pdfDataList
            let base64 = value.fileValue //獲得bas464編碼
            let decodedBase64 = atob(base64) //使用瀏覽器自帶的方法解碼
            let pdf = await  PDFJS.getDocument({data: decodedBase64}) //返回一個pdf對象
            let pages = pdf.numPages //聲明一個pages變量等于當前pdf文件的頁數(shù)
            for (let i = 1; i <= pages; i++) { //循環(huán)頁數(shù)
              let canvas = document.createElement('canvas') 
              let page = await pdf.getPage(i) //調(diào)用getPage方法傳入當前循環(huán)的頁數(shù),返回一個page對象
              let scale = 1;//縮放倍數(shù)遭殉,1表示原始大小
              let viewport = page.getViewport(scale); 
              let context = canvas.getContext('2d'); //創(chuàng)建繪制canvas的對象
              canvas.height = viewport.height; //定義canvas高和寬
              canvas.width = viewport.width;
              let renderContext = {
                canvasContext: context,
                viewport: viewport
              };
              await page.render(renderContext)

              canvas.className = 'canvas' //給canvas節(jié)點定義一個class名,這里我取名為canvas
              pdfList.appendChild(canvas) //插入到pdfList節(jié)點的最后
            }
        }
    }
   

至此頁面上就會多出一個canvas節(jié)點并且顯示當前pdf文件的第一頁的圖片,如果當前pdf文件有多頁就會渲染出多個canvas節(jié)點,有多個pdf文件就會先循環(huán)外層,然后再循環(huán)內(nèi)層,把每個pdf文件的每一頁都生成一個canvas節(jié)點


修改樣式

渲染出頁面后還有個要注意的點,Vue框架會給每個組件的DOM節(jié)點生成一個自定義屬性,而節(jié)點動態(tài)生成的canvas節(jié)點,并沒有data-v-xxxxx這樣的自定義屬性



而Vue會給每個組件里面的樣式添加這個自定義屬性,Vue框架這樣做可以防止樣式的相互污染(也就是style旁邊的scoped屬性)


我們這里可以在這個style下面再創(chuàng)建一個style寫入樣式來達到修改canvas樣式的效果,但是記得這樣做你整個項目里面類名叫canvas的都會獲得這個樣式,需要注意


寫在最后

這里使用的是動態(tài)生成canvas節(jié)點然后渲染這個節(jié)點生成的圖片,然而直接使用createElement生成一個節(jié)點并且頻繁操作DOM會對性能有一定的影響,如果有更好的方法歡迎留言交流,感謝觀看

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末枫甲,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌斑胜,老刑警劉巖鞠抑,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件饭聚,死亡現(xiàn)場離奇詭異,居然都是意外死亡搁拙,警方通過查閱死者的電腦和手機秒梳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來箕速,“玉大人酪碘,你說我怎么就攤上這事⊙尉ィ” “怎么了兴垦?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長庭呜。 經(jīng)常有香客問我滑进,道長,這世上最難降的妖魔是什么募谎? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任扶关,我火速辦了婚禮,結果婚禮上数冬,老公的妹妹穿的比我還像新娘节槐。我一直安慰自己,他們只是感情好拐纱,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布铜异。 她就那樣靜靜地躺著,像睡著了一般秸架。 火紅的嫁衣襯著肌膚如雪揍庄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天东抹,我揣著相機與錄音蚂子,去河邊找鬼沃测。 笑死,一個胖子當著我的面吹牛食茎,可吹牛的內(nèi)容都是我干的蒂破。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼别渔,長吁一口氣:“原來是場噩夢啊……” “哼附迷!你這毒婦竟也來了?” 一聲冷哼從身側響起哎媚,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤喇伯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后拨与,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體艘刚,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年截珍,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片箩朴。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡岗喉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出炸庞,到底是詐尸還是另有隱情钱床,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布埠居,位于F島的核電站查牌,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏滥壕。R本人自食惡果不足惜纸颜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望绎橘。 院中可真熱鬧胁孙,春花似錦、人聲如沸称鳞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽冈止。三九已至狂票,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間熙暴,已是汗流浹背闺属。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工慌盯, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人屋剑。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓润匙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親唉匾。 傳聞我的和親對象是個殘疾皇子孕讳,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容