Проект Vue pdf (base64) в изображение

внешний интерфейс JavaScript Canvas

У компании есть бизнес-требование, которое требует, чтобы бэкенд передал кодировку pdf base64 на фронтенд, а фронтенд выводит его на интерфейс.Позже я искал много статей в Интернете о преобразовании base64 в pdf, но они не очень детализированы, и встречались в процессе реализации.Ям много.После дня исследований я, наконец, понял эту функцию.Позвольте мне поделиться проблемами и решениями, с которыми я столкнулся в этой функции.

Следует отметить, что основным подключаемым модулем, используемым здесь, является pdf.js, Принцип заключается в динамическом создании тегов холста, а затем создании объекта, который может отображать PDF через pdf.js, а затем отображать каждый холст и сгенерированный PDF это форма изображения, а не элементы управления, такие как pdf

Внедрить плагины

Во многих блогах здесь используются нативные методы JavaScript для импорта pdf.js, например, использование тегов скрипта для импорта внешних скриптов js или прямое копирование исходного кода pdf.js в проект, но когда я пробую эти методы, они не особенно просты. использовать. , и после введения объем проекта слишком велик,

Затем я пошел на github, чтобы найти способ представить pdf.js через менеджер пакетов, вpdf.jsОфициальное описание на гитхабе - как использовать pdf.js с gulp, но подробного описания для npm нет, и наконец я нашел такое предложение между строк

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-распределение

То, что передается мне в фоновом режиме, представляет собой массив объектов, состоящий из имени pdf-файла и кодировки base64 pdf, которую я назвал pdfDataList.

Вы можете видеть, что fileName — это имя pdf-файла, fileVale — это кодировка base64 файла pdf, а thumbnail — это кодировка base64 миниатюры pdf. В соответствии с кодом шаг за шагом вы можете визуализировать холст)

  1. Сначала мы создаем родительский узел, в котором размещаются все узлы холста, с именем pdfList.

  2. Затем создайте асинхронную функцию showPdf(Если вы не знаете, что такое асинхронная функция, вы можете проверить async/await, вы можете использовать метод promise.then без асинхронной функции, но async/await — идеальное решение для асинхронных операций.)

async showPdf() {
       
    }
  1. Используйте querySelector, чтобы выбрать узел dom с именем класса pdfList, а затем просмотреть каждый элемент массива pdfDataList, переданный из фона.Здесь собственный метод браузера atob() используется для декодирования base64, что объясняется на MDN:

вы можете использовать window.btoa()метод для кодирования данных, которые могут иметь проблемы во время передачи, и после принятия данных используйте метод atob() для декодирования данных.

Синтаксис: var decodedData = scope.atob(encodedData);

Затем вызовите метод getDocument плагина pdf.js, getDocument — это обещание, поэтому, если вы используете асинхронную функцию, вам нужно добавить ключевое слово await (Если вы не используете асинхронные функции, добавьте .then ((pdf) => {.......}) после метода. Этот объект PDF совпадает с моим объектом PDF, а ошибка асинхронной работы не рассматривается Вот на данный момент. Если есть требование, вы можете добавить улов, чтобы поймать ошибку) Параметр метода getDocument — объект, ключ объекта — данные, а значение — декодированное значение base64.Этот метод возвращает объект pdf.Этот объект имеет несколько свойств, которые можно распечатать и наблюдать.

Здесь мы сначала используем свойство numPages, которое указывает, сколько страниц в текущем pdf-файле.

async showPdf() {
        let pdfList = document.querySelector('.pdfList') //通过querySelector选择DOM节点,使用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文件的页数
        }
    }
  1. После получения объекта текущего файла PDF и всех страниц текущего файла PDF выполните цикл по каждому номеру страницы и выполните следующие операции:

1) Динамически создавать узлы холста

2) Вызовите метод getPage() и метод getViewport() для прототипа объекта pdf и передайте количество страниц в текущем цикле и размер масштабирования холста по очереди (вы можете копировать и вставлять напрямую, если ты тут не понял)

3) визуализировать текущий узел холста

4) Вызовите метод render() объекта страницы для отображения текущей страницы, этот метод также является обещанием, вам нужно использовать ключевое слово await, чтобы дождаться разрешения состояния перед выполнением кода после

5) Дайте узлу холста, отображающему текущую страницу, имя класса в качестве холста, чтобы легко изменить стиль, и, наконец, вставьте узел холста в узел pdfList.

async showPdf() {
        let pdfList = document.querySelector('.pdfList')  //通过querySelector选择DOM节点,使用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文件的页数
            for (let i = 1; i <= pages; i++) { //循环页数
              let canvas = document.createElement('canvas') 
              let page = await pdf.getPage(i) //调用getPage方法传入当前循环的页数,返回一个page对象
              let scale = 1;//缩放倍数,1表示原始大小
              let viewport = page.getViewport(scale); 
              let context = canvas.getContext('2d'); //创建绘制canvas的对象
              canvas.height = viewport.height; //定义canvas高和宽
              canvas.width = viewport.width;
              let renderContext = {
                canvasContext: context,
                viewport: viewport
              };
              await page.render(renderContext)
              canvas.className = 'canvas' //给canvas节点定义一个class名,这里我取名为canvas
              pdfList.appendChild(canvas) //插入到pdfList节点的最后
            }
            
        }
    }
   

В этот момент на странице будет еще один узел холста, и будет отображаться изображение первой страницы текущего pdf-файла.Если текущий pdf-файл имеет несколько страниц, будет отображаться несколько узлов холста.Если их несколько pdf, сначала зацикливается внешний слой, затем зацикливается внутренний слой и создается узел холста для каждой страницы каждого pdf-файла.

Изменить стиль

Есть еще один момент, на который следует обратить внимание после рендеринга страницы: платформа Vue сгенерирует настраиваемый атрибут для узла DOM каждого компонента, а узел холста, динамически сгенерированный узлом, не имеет настраиваемого атрибута, такого как data-v- ххххх

Vue добавит этот настраиваемый атрибут к стилям в каждом компоненте.Фреймворк Vue делает это, чтобы предотвратить взаимное загрязнение стилей (то есть атрибут scoped рядом со стилем).

Мы можем создать другой стиль написания стиля под этим стилем, чтобы добиться эффекта изменения стиля холста, но не забудьте сделать это, имя класса холст во всем вашем проекте получит этот стиль, вам нужно обратить внимание

напиши в конце

Здесь используется динамическое создание узла холста, а затем рендеринг изображения, созданного этим узлом. Однако непосредственное использование createElement для создания узла и частая работа с DOM окажут определенное влияние на производительность. Если есть лучший способ, пожалуйста, оставьте сообщение для обмена, спасибо за просмотр

постскриптум

В предыдущем коде мы просматриваем каждую страницу сгенерированного объекта PDF, а затем динамически генерируем узел холста, что заставит браузер повторно отображать новую информацию.Вы можете использовать documentFragment для оптимизации отображения узла холста.

Синтаксис: let fragment = document.createDocumentFragment();

documentFragment создаст пустой фрагмент документа, который похож на «склад», который может временно хранить сгенерированные нами узлы, а затем одновременно добавлять их к родительскому узлу, что уменьшает количество отрисовок и повышает производительность до определенного Теперь изменим предыдущий код, добавим documentFragment

async showPdf() {
        let pdfList = document.querySelector('.pdfList')
        let fragment = document.createDocumentFragment() //生成一个空的documentFragment文档片段 //创建documentFragment储存canvas节点一次性渲染//通过querySelector选择DOM节点,使用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文件的页数
            for (let i = 1; i <= pages; i++) { //循环页数
              let canvas = document.createElement('canvas') 
              let page = await pdf.getPage(i) //调用getPage方法传入当前循环的页数,返回一个page对象
              let scale = 1;//缩放倍数,1表示原始大小
              let viewport = page.getViewport(scale); 
              let context = canvas.getContext('2d'); //创建绘制canvas的对象
              canvas.height = viewport.height; //定义canvas高和宽
              canvas.width = viewport.width;
              let renderContext = {
                canvasContext: context,
                viewport: viewport
              };
              await page.render(renderContext)
              canvas.className = 'canvas' //给canvas节点定义一个class名,这里我取名为canvas
              fragment.appendChild(canvas) //添加canvas节点到fragment文档片段中
            }
             pdfList.appendChild(fragment) //将fragment插入到pdfList节点的最后
        }
    }