整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          前端實現文件預覽(pdf、excel、word、圖片)

          前端實現文件預覽功能

          需求:實現一個在線預覽pdf、excel、word、圖片等文件的功能。
          介紹:支持pdf、xlsx、docx、jpg、png、jpeg。
          以下使用Vue3代碼實現所有功能,建議以下的預覽文件標簽可以在外層包裹一層彈窗。

          圖片預覽

          iframe標簽能夠將另一個HTML頁面嵌入到當前頁面中,我們的圖片也能夠使用iframe標簽來進行展示。

          <iframe  :src="圖片地址"
                   style="z-index: 1000; height:650px; width: 100%; margin: 0 auto"
                   sandbox="allow-scripts allow-top-navigation allow-same-origin allow-popups"
          />

          sandbox 這個屬性如果是單純預覽圖片可以不使用,該屬性對呈現在 iframe 框架中的內容啟用一些額外的限制條件。屬性值可以為空字符串(這種情況下會啟用所有限制),也可以是用空格分隔的一系列指定的字符串。

        1. allow-scripts: 允許嵌入的瀏覽上下文運行腳本(但不能創建彈窗)。如果沒有使用該關鍵字,就無法運行腳本。
        2. allow-top-navigation: 允許將框架內所加載頁面中的超鏈接導航到父級窗口
        3. allow-same-popups: 允許彈窗 (例如 window.open, target="_blank")。如果沒有使用該關鍵字,相應的功能將自動被禁用。
        4. allow-same-origin: 如果沒有使用該關鍵字,嵌入的瀏覽上下文將被視為來自一個獨立的源,這將使 same-origin policy 同源檢查失敗。使用了這個屬性,那么當前頁面和iframe打開的頁面視為同源。
        5. word文檔預覽(docx)

          先下載npm包
          npm i docx-preview --save


          <div class="docxRef"></div>
          
          <script>
          import { renderAsync } from 'docx-preview';
          
          function fn() {
          // 這里的res.data是 blob文件流,如果自己的不是blob文件流
          // 可以通過URL.createObjectURL(參數) 參數為File格式,轉換為blob文件流
              let blob = res.data
              let childRef = document.getElementsByClassName('docxRef');
              renderAsync(blob, childRef[0]) //渲染
          }
          fn()
          
          </script>

          blob文件流

          預覽excel文件(xlsx)

          下載包
          npm install xlsx@0.16.0


          <div class="xlsxClass"></div>
          const reader = new FileReader();
          //通過readAsArrayBuffer將blob轉換為ArrayBuffer對
          reader.readAsArrayBuffer(res.data) // 這里的res.data是blob文件流
          reader.onload = (event) => {
            // 讀取ArrayBuffer數據變成Uint8Array
            var data = new Uint8Array(event.target.result);
            // 這里的data里面的類型和后面的type類型要對應
            var workbook = XLSX.read(data, { type: "array" });
            var sheetNames = workbook.SheetNames; // 工作表名稱
            var worksheet = workbook.Sheets[sheetNames[0]];
            // var excelData = XLSX.utils.sheet_to_json(worksheet); //JSON
            let html = XLSX.utils.sheet_to_html(worksheet);
            document.getElementsByClassName('xlsxClass')[0].innerHTML = html
          };

          pdf預覽

          下載包 npm install pdfjs-dist
          我使用的是npm install pdfjs-dist@2.0.943版本,以下例子使用的是vue3+vite創建的項目
          以下例子通過canvas來渲染pdf


          <template>
            <div class="box">
              <div class="tool-bar">
                <div>{{ pdfParams.pageNumber }} / {{ pdfParams.total }}</div>
                <button type="primary" :disabled="pdfParams.pageNumber == pdfParams.total" @click="nextPage">下一頁
                </button>
                <button type="primary" :disabled="pdfParams.pageNumber == 1" @click="prevPage">上一頁</button>
              </div>
              <canvas id="pdf-render"></canvas>
            </div>
          </template>
          


          <script setup>
          import { onMounted, ref, reactive } from 'vue'
          const pdfParams = reactive({
            pageNumber: 1, // 當前頁
            total: 0, // 總頁數
          });
          
          // 不要定義為ref或reactive格式,就定義為普通的變量
          let pdfDoc = null;
          // 這里必須使用異步去引用pdf文件,直接去import會報錯,也不知道為什么
          onMounted(async ()=> {
            let pdfjs = await import('pdfjs-dist/build/pdf')
            let pdfjsWorker = await import('pdfjs-dist/build/pdf.worker.entry')
            pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker
            // 此文件位于public/test2.pdf
            let url = ref('/test2.pdf')
            pdfjs.getDocument(url.value).promise.then(doc => {
              pdfDoc = doc
              pdfParams.total = doc.numPages
              getPdfPage(1)
            })
          })
          
          // 加載pdf的某一頁
          const getPdfPage = (number) => {
            pdfDoc.getPage(number).then(page => {
              const viewport = page.getViewport()
              const canvas = document.getElementById('pdf-render')
              const context = canvas.getContext('2d')
              canvas.width = viewport.viewBox[2]
              canvas.height = viewport.viewBox[3]
              viewport.width = viewport.viewBox[2]
              viewport.height = viewport.viewBox[3]
              canvas.style.width = Math.floor(viewport.width) + 'px'
              canvas.style.height = Math.floor(viewport.height) + 'px'
          
              let renderContext = {
                canvasContext: context,
                viewport: viewport,
                // 這里transform的六個參數,使用的是transform中的Matrix(矩陣)
                transform: [1, 0, 0, -1, 0, viewport.height]
              }
              // 進行渲染
              page.render(renderContext)
            })
          }
          // 下一頁功能
          const prevPage = () => {
            if(pdfParams.pageNumber > 1) {
              pdfParams.pageNumber -= 1
            } else {
              pdfParams.pageNumber  = 1
            }
            getPdfPage(pdfParams.pageNumber)
          }
          // 上一頁功能
          const nextPage = () => {
            if(pdfParams.pageNumber < pdfParams.total) {
              pdfParams.pageNumber += 1
            } else {
              pdfParams.pageNumber = pdfParams.total
            }
            getPdfPage(pdfParams.pageNumber)
          }
          </script>

          以上注意點:

          • 必須異步引用pdf的文件!!!
          • pdf演示文件位于public/test2.pdf
          • transform: [1, 0, 0, -1, 0, viewport.height],使用了transform中的Matrix(矩陣)
          • 下一頁和上一頁功能都需要重新渲染


          原文鏈接:https://juejin.cn/post/7246609845272395837

          控系統完整html頁面,適合用來做監控類型.基于Bootstrap的完美后臺管理框架,開箱即用

          直接導入Eclipse或IDEA等常用開發工具,頁面已經做過加載優化,常用業務頁面demo數據均已存在,可直接預覽,可對接系統后臺直接使用。

          效果圖

          項目地址

          https://gitee.com/kangjie1209/monitor

          筆者之前的文章里介紹過一個代碼在線編輯預覽工具的實現(傳送門:快速搭建一個代碼在線編輯預覽工具),實現了csshtmljs的編輯,但是對于demo場景來說,vue單文件也是一個比較好的代碼組織方式,至少筆者就經常在寫vue項目的同時順便寫寫各種demo,但是分享不太方便,因為單文件不能直接運行看效果,基于此,筆者決定在之前的基礎上再增加一個vue單文件的編輯及預覽功能。

          ps.如果沒看過之前的文章也沒關系,這里簡單介紹一下該項目:code-run,基本框架使用的是vue3.x版本, 構建工具使用的是vite,代碼編輯器使用的是monaco-editor,基本原理就是把cssjshtml拼接成完整的html字符串扔到iframe里進行預覽。

          另外項目目前存在一些坑:

          1.vite不支持使用commonjs模塊(筆者尚未找到解決方法,知道的朋友在評論區留個言?)。

          2.三方模塊目前都放在項目的public文件夾下,作為靜態資源按需加載:

          另外由于Monaco Editor自帶的模塊系統和defined/require沖突,導致目前需要手動修改各個三方模塊,讓它只支持全局對象的方式來使用,比如:

          基本思路

          想要預覽vue單文件,其實就是要想辦法轉成瀏覽器能認識的cssjshtml。首先想到的是使用vue-loader來轉換,但是看了它的文檔,發現還是必須要配合webpack才能使用,不過筆者發現了一個配套的模塊vue-template-compiler,它提供了一些方法,其中有一個parseComponent方法可以用來解析vue單文件,輸出各個部分的內容,輸出結構如下:

          所以思路就很清晰了:

          1.html部分,結構固定為:

          <div id="app"></div>

          2.css部分,首先判斷有沒有使用css預處理器,有的話就先使用對應的解析器轉換成css,然后再通過style標簽插入到頁面。

          3.js部分,以vue2.x版本為例,我們最終需要生成如下所示的結構:

          new Vue({
              el: '#app',
              template: '',// 模板部分內容
              // ...其他選項
          })

          其他選項就是vue-template-compiler解析出的script.content內容,但是單文件里基本都是export default {}形式的;template選項很簡單,就是template.content的內容。

          這里的處理思路是通過babel來將export default {}的形式轉換成new Vue的形式,然后再添加上eltemplate兩個屬性即可,這會通過寫一個babel插件來實現。

          安裝及使用vue-template-compiler

          首先vue-template-compiler模塊我們也會把它放到public文件夾下,那么它的瀏覽器使用版本在哪呢?我們可以先安裝它:npm i vue-template-compiler,然后在node_modules里找到它,會發現其中有一個文件:

          這個就是我們要的,直接把它復制到public文件夾下(當然也要注釋掉它的模塊導出),然后再把該模塊刪除即可,之后我們便可以通過全局對象使用它:

          // code就是vue單文件內容字符串
          let componentData = window.VueTemplateCompiler.parseComponent(code)
          // 處理style、script、template三部分的內容,最后生成css字符串、js字符串、html字符串
          parseVueComponentData(componentData)

          生成html字符串

          html部分我們要做的就是寫死一個div,用它來掛載vue實例即可:

          const parseVueComponentData = async (data) => {
              // html就直接渲染一個掛載vue實例的節點
              let htmlStr = `<div id="app"></div>`
          
              return {
                  html: htmlStr
              }
          }

          生成css字符串

          style部分如果沒有使用css預處理器的話那么也很簡單,直接返回樣式內容即可,否則需要先使用對應的預處理器把它轉換成css

          const parseVueComponentData = async (data) => {
              // 編譯css
              let cssStr = []
              // vue單文件的style塊可以存在多個,所以解析出的styles是個數組
              for(let i = 0; i < data.styles.length; i++) {
                  let style = data.styles[i]
                  // 如果使用了css預處理器,lang字段不為空
                  let preprocessor = style.lang || 'css'
                  if (preprocessor !== 'css') {
                      // load方法會去加載對應的三方解析模塊,詳情請閱讀上一篇文章
                      await load([preprocessor])
                  }
                  // css方法會使用對應的解析器來解析,可參考之前的文章
                  let cssData = await css(preprocessor, style.content)
                  // 把解析后的css字符串添加到結果數組里
                  cssStr.push(cssData)
              }
              return {
                  // 最后把多個style塊的css拼接成一個
                  css: cssStr.join('\r\n')
              }
          }

          上面的css會調用對應的css預處理器的解析模塊來編譯,比如less的處理如下:

          const css = (preprocessor, code) => {
              return new Promise((resolve, reject) => {
                  switch (preprocessor) {
                      case 'css':
                          resolve(code)
                          break;
                      case 'less':
                          window.less.render(code)
                              .then((output) => {
                                  resolve(output.css)
                              },
                              (error) => {
                                  reject(error)
                              });
                          break;
                  }
              })
          }

          生成js字符串

          script部分的內容我們會使用babel來編譯:

          const parseVueComponentData = async (data, parseVueScriptPlugin) => {
              // babel編譯,通過編寫插件來完成對ast的修改
              let jsStr = data.script ? window.Babel.transform(data.script.content, {
                  presets: [
                      'es2015',
                      'es2016',
                      'es2017',
                  ],
                  plugins: [
                      // 插件
                      parseVue2ScriptPlugin(data)
                  ]
              }).code : ''
          
              return {
                  js: jsStr
              }
          }

          babel插件其實就是一個函數,接收一個babel對象作為參數,然后需要返回一個對象,我們可以在該對象的visitor屬性里訪問到AST節點,并進行一些修改,visitor中的每個函數都接收2個參數:pathstatepath表示兩個節點之間連接的對象,包含節點信息及一些操作方法,插件開發的詳細文檔請參考:plugin-handbook。

          基本結構如下:

          const parseVue2ScriptPlugin = (data) => {
              return function (babel) {
                  let t = babel.types
                  return {
                      visitor: {
          
                      }
                  }
              }
          }

          轉換export default語法

          接下來再次明確我們的需求,我們要把export default {}的形式轉換成new Vue的形式,具體怎么做呢,我們可以使用astexplorer這個工具先看看這兩種結構的AST的差別是什么:

          可以發現黃色部分都是一樣的,只是外層的節點不一樣,所以我們可以訪問ExportDefaultDeclaration節點,然后把它替換成ExpressionStatement就行了,創建新節點也很簡單,參考AST及babel-types


          主站蜘蛛池模板: 国产欧美色一区二区三区 | 无码av免费一区二区三区| 国产视频一区二区在线观看| 国产精品视频一区麻豆| 日本一区免费电影| 国产另类ts人妖一区二区三区| 真实国产乱子伦精品一区二区三区 | 日韩精品无码一区二区视频| 免费精品一区二区三区第35| 精品一区二区三区免费毛片爱| 亚洲AV成人一区二区三区在线看 | 国产福利电影一区二区三区久久久久成人精品综合 | 国产精品一区视频| 色视频综合无码一区二区三区| 国产亚洲日韩一区二区三区| 久久毛片一区二区| 夜色阁亚洲一区二区三区| 无码午夜人妻一区二区三区不卡视频| 久久久av波多野一区二区| 在线一区二区观看| 国产精品高清一区二区三区| 性色av闺蜜一区二区三区| 国产综合精品一区二区三区| 久热国产精品视频一区二区三区| 亚洲成av人片一区二区三区| 久久精品国产第一区二区三区| 无遮挡免费一区二区三区| 久久er99热精品一区二区| 日韩动漫av在线播放一区| 中文字幕日韩丝袜一区| 国产亚洲自拍一区| 人妻夜夜爽天天爽一区| 一区二区三区视频在线观看| 色综合视频一区二区三区| 精品无码一区二区三区在线| 一本大道东京热无码一区| 国产a久久精品一区二区三区| 91亚洲一区二区在线观看不卡| 本免费AV无码专区一区| 国产伦精品一区二区三区免费下载| 蜜桃臀无码内射一区二区三区|