1. Предпосылки
В процессе разработки необходимо реализовать функцию генерации PDF из веб-страниц.Сгенерированный PDF необходимо загрузить на сервер, а адрес PDF используется в качестве параметра для запроса внешнего интерфейса.Этот процесс преобразования и преобразованный PDF-файл не нужно отображать пользователю во внешнем интерфейсе.
2. Технический отбор
Эту функцию не нужно отображать пользователю на фронтэнде.Для экономии ресурсов клиента выберите реализацию функции генерации PDF из веб-страниц на стороне сервера.
1. Puppeteer
PuppeteerЯвляетсяNode
библиотека, которая предоставляет расширенныеAPI
пройтиDevTools
Управление протоколомChrome
илиChromium
.
Большинство действий, которые вы можете выполнять вручную в браузере, можно выполнить с помощьюPuppeteer
сделано, например:
- Создание скриншотов и PDF-файлов страниц;
- Ползание
SPA
и генерировать предварительно обработанный контент (т.SSR
); - Автоматизировать отправку форм, тестирование пользовательского интерфейса, ввод с клавиатуры и т. д.;
- Создайте современную автоматизированную тестовую среду. использовать последнюю
JavaScript
и функциональность браузера непосредственно в последней версииChrome
запустить тест в; - Захватите сайты трассировки временной шкалы, чтобы помочь диагностировать проблемы с производительностью;
- тестовое задание
Chrome
расширение.
Сверху видно,Puppeteer
может быть реализован вNode
Функция PDF, которая создает страницы сбоку.
3. Этапы реализации
1. Установка
войти в проект, установитьpuppeteer
к местному.
$ npm install -g cnpm --registry=https://registry.npm.taobao.org
$ cnpm i puppeteer --save
Следует отметить, что установкаpuppeteer
загружается сAPI
последняя версия, используемая сChromium
Браузер, существуют следующие способы изменить настройки по умолчанию без загрузки браузера:
- существуетпеременная средыустановить в
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD
; - использовать
puppeteer-core
заменятьpuppeteer
.
puppeteer-core
даpuppeteer
Облегченная версия не загружает браузер по умолчанию, а запускает существующий браузер или подключается к удаленному браузеру, используяpuppeteer-core
Следует отметить наличие локального подключаемого браузера и установленногоpuppeteer-core
Версия совместима с браузером, предназначенным для подключения. Способ подключения к локальному браузеру следующий:
const browser = await puppeteer.launch({
executablePath: '/path/to/Chrome'
});
Этот проект необходимо развернуть на сервере, нет подключаемого браузера, поэтому выберите установкуpuppeteer
.
2. Запустите браузер
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--font-render-hinting=medium']
})
headless
Представляет безголовый режим, запустите браузер на бэкэнде, и на фронтенде не будет отображения.
Небольшое предложение: при локальной отладке рекомендуется установить
headless: false
, вы можете запустить полную версию браузера и просматривать содержимое прямо в окне браузера.
3. Откройте новую страницу
После создания браузера откройте новую страницу в браузере.
const page = await browser.newPage()
4. Перейти на указанную страницу
Перейдите на страницу, на которой вы хотите создать PDF-файл.
await page.goto(`${baseURL}/article/${id}`, {
timeout: 60000,
waitUntil: 'networkidle2', // networkidle2 会一直等待,直到页面加载后不存在 2 个以上的资源请求,这种状态持续至少 500 ms
})
timeout
это самое длинное время загрузки, по умолчанию 30 секунд, когда время загрузки страницы велико, рекомендуется установитьtimeout
Измените значение на большее, чтобы предотвратить ошибку тайм-аута.
waitUntil
Указывает, насколько загружена страница для начала создания PDF-файла или других операций.Если на веб-страницу загружается много графических ресурсов, рекомендуется установить значениеnetworkidle2
, со следующими необязательными значениями:
- нагрузка: когда
load
Когда событие срабатывает; - domcontentloaded: когда
DOMContentLoaded
Когда событие срабатывает; - networkidle0: не более 0 запросов ресурсов после загрузки страницы, и это состояние длится не менее 500 мс;
- networkidle2: не более 2 запросов ресурсов после загрузки страницы, и это состояние длится не менее 500 мс.
5. Укажите путь для создания pdf
После загрузки указанной выше страницы для этой страницы создается PDF-файл.
const ext = '.pdf'
const key = randomFilename(title, ext)
const _path = path.resolve(config.uploadDir, key)
await page.pdf({ path: _path, format: 'a4' })
path
Указывает путь к файлу для сохранения PDF-файла. Если путь не указан, PDF-файл не будет сохранен на диск.
Небольшое предложение: независимо от того, нужно ли сохранять PDF локально, рекомендуется задать путь при отладке, чтобы было удобно просматривать стиль сгенерированного PDF и проверять, нет ли проблем.
format
Указывает формат бумаги для PDF, размер a4 составляет 8,27 дюймов x 11,7 дюймов, что является традиционным размером печати.
Примечание. В настоящее время поддерживается только безголовый режим: true для создания PDF в автономном режиме.
6. Закройте браузер
После завершения всех операций закройте браузер для экономии производительности.
await browser.close()
4. Трудности
1. Отложенная загрузка изображения
Поскольку страница, которая должна создать PDF-файл, является страницей типа статьи, она содержит много изображений, а изображения загружаются отложенно, поэтому сгенерированный PDF-файл будет иметь много нижних карт с отложенной загрузкой, и эффект будет следующим:
Решение состоит в том, чтобы прокрутить страницу вниз после перехода на страницу, будут запрошены все ресурсы изображения,waitUntil
Установить какnetworkidle2
, изображение может быть успешно загружено.
await autoScroll(page) // 因为文章图片引入了懒加载,所以需要把页面滑动到最底部,保证所有图片都加载出来
/**
* 控制页面自动滚动
* */
function autoScroll (page) {
return page.evaluate(() => {
return new Promise<void>(resolve => {
let totalHeight = 0
const distance = 100
// 每200毫秒让页面下滑100像素的距离
const timer = setInterval(() => {
const scrollHeight = document.body.scrollHeight
window.scrollBy(0, distance)
totalHeight += distance
if (totalHeight >= scrollHeight) {
clearInterval(timer)
resolve()
}
}, 200)
})
})
}
используется здесьpage.evaluate()
метод, используемый для управления операциями на странице, такими как использование встроенногоDOM
селектор, использоватьwindow
метод и так далее.
2. Стили печати CSS
согласно сОфициальный сайтпроиллюстрировать,page.pdf()
Стиль сгенерированного PDF-файла создаетсяprint css media
указано, поэтому может быть переданоcss
Чтобы изменить стиль сгенерированного PDF-файла, взяв в качестве примера требования этой статьи, в сгенерированном PDF-файле необходимо скрыть заголовок, нижнюю часть и другие части, не относящиеся к основной части статьи.Код выглядит следующим образом. :
@media print {
.other_info,
.authors,
.textDetail_comment,
.detail_recTitle,
.detail_rec,
.SuspensePanel {
display: none !important;
}
.Footer,
.HeaderSuctionTop {
display: none;
}
}
3. Состояние входа
Так как есть некоторые статьи, которые не открыты для внешних пользователей, пользователи должны быть аутентифицированы, а пользователи, соответствующие требованиям, могут видеть содержимое статьи, поэтому после перехода на указанную страницу статьи вам необходимо внедрить состояние входа в сгенерированное окно браузера и квалифицированный логин Только пользователи могут видеть содержание этой части статьи.
инъекцияcookie
способ получить статус входа, используйтеpage.evaluate()
настраиватьcookie
, код показан ниже:
async function simulateLogin (page, cookies, domain) {
return await page.evaluate((sig, sess, domain) => {
let date = new Date()
date = new Date(date.setDate(date.getDate() + 1))
let expires = ''
expires = `; expires=${date.toUTCString()}`
document.cookie = `koa:sess.sig=${sig}${expires}; domain=${domain}; path=/`
document.cookie = `koa:sess=${sess}=${expires}; domain=${domain}; path=/` // =是这个cookie的value
document.cookie = `is_login=true${expires}; domain=${domain}; path=/`
}, cookies['koa:sess.sig'], cookies['koa:sess'], domain)
}
await simulateLogin(page, cookies, config.domain.split('//')[1])
Небольшое предложение:
Puppeteer
Также поставляется сapi
выполнитьcookie
вводить, какpage.setCookie({name: name, value: value})
, но мне не удалось получить состояние входа в систему, внедрив таким образом, и я не нашел конкретной причины.Рекомендуется использовать вышеуказанный метод для прямой инъекции.cookie
, обратите внимание наname
а такжеvalue
за пределами,expires
,domain
,path
Также требуется настройка.
4. Докер развертывает Puppeteer
В соответствии с приведенной выше операцией страница может быть успешно сгенерирована в формате PDF локально.После того, как локальный опыт не будет проблемой, ее необходимо развернуть на сервере для тестирования и онлайн.
без модификацииDockerfile
, после развертывания была обнаружена следующая ошибка:
На официальном сайте естьИнструкции по настройке докераДоступно для справки, финальная практика доступнаubuntu
систематическийDockerfile
следующим образом:
# ...省略...
# 安装 puppeteer 依赖
RUN apt-get update && \
apt-get install -y libgbm-dev && \
apt-get install gconf-service libasound2 libatk1.0-0 libatk-bridge2.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget build-essential libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev -y && \
apt-get install -y fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf --no-install-recommends
# ...省略...
Просто нужно сосредоточитьсяУстановить зависимости кукловодачасть.
Примечание. До версии 1.18.1 для Puppeteer требовалось как минимум Node версии 6.4.0. Версии от v1.18.1 до v2.1.0 зависят от Node 8.9.0+. Начиная с версии 3.0.0, Puppeteer начинает зависеть от Node 10.18.1+. При настройке Dockerfile также необходимо обращать внимание на нод-версию сервера.
V. Резюме
В этой статье описывается реализация вNode
Полный процесс создания файла PDF из веб-страницы на стороне клиента сводится к следующим трем пунктам:
- Выбор технологии, выбор соответствующих средств для реализации функций в соответствии со сценариями спроса;
- читатьофициальная документация, просмотрите документ быстро, чтобы не столкнуться с некоторыми ямами;
- Для решения трудности используйте неиспользованный инструмент, вы столкнетесь с нерешенными проблемами, давайте разберем его ^^.
Ссылаться наИсходный код демоВы можете быстро начать работу с вышеуказанными функциями, я надеюсь, что эта статья поможет вам, спасибо за чтение ❤️
· Прекрасное прошлое ·
[Обзор прямой трансляции · Рост и трансформация Чэн Юаня]