@kne/fastify-puppeteer 1.1.8 → 1.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/libs/controllers.js +27 -0
- package/libs/core/setup.js +12 -2
- package/libs/services.js +121 -70
- package/package.json +1 -1
package/libs/controllers.js
CHANGED
|
@@ -278,6 +278,33 @@ module.exports = fp(async (fastify, options) => {
|
|
|
278
278
|
}
|
|
279
279
|
);
|
|
280
280
|
|
|
281
|
+
fastify.post(
|
|
282
|
+
`${options.prefix}/parseUrlToPdfBatch`,
|
|
283
|
+
{
|
|
284
|
+
onRequest: options.authenticate,
|
|
285
|
+
schema: {
|
|
286
|
+
description: '接口说明',
|
|
287
|
+
summary: 'url生成pdf文件流',
|
|
288
|
+
body: {
|
|
289
|
+
type: 'object',
|
|
290
|
+
required: ['urlList'],
|
|
291
|
+
properties: {
|
|
292
|
+
options: pdfSchema,
|
|
293
|
+
urlList: { type: 'array', items: { type: 'string' } }
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
async (request, reply) => {
|
|
299
|
+
const filename = await services.parseUrlToPdfBatch({
|
|
300
|
+
urlList: request.body.urlList,
|
|
301
|
+
options: request.body.options
|
|
302
|
+
});
|
|
303
|
+
request.body.filename && reply.header('Content-Disposition', `attachment; filename=${encodeURIComponent(request.body.filename)}`);
|
|
304
|
+
return reply.sendFile(filename, { root: options.root });
|
|
305
|
+
}
|
|
306
|
+
);
|
|
307
|
+
|
|
281
308
|
fastify.get(
|
|
282
309
|
`${options.prefix}/parseUrlToPdfBatch`,
|
|
283
310
|
{
|
package/libs/core/setup.js
CHANGED
|
@@ -6,10 +6,20 @@ module.exports = async options => {
|
|
|
6
6
|
const browser = await puppeteer.launch(options.puppeteerOptions);
|
|
7
7
|
const deferred = createDeferred(options.maxTaskSize);
|
|
8
8
|
return {
|
|
9
|
-
task: async callback => {
|
|
9
|
+
task: async (callback, pageOptions) => {
|
|
10
|
+
pageOptions = Object.assign(
|
|
11
|
+
{},
|
|
12
|
+
{
|
|
13
|
+
timezone: 'Asia/Shanghai',
|
|
14
|
+
viewport: {}
|
|
15
|
+
},
|
|
16
|
+
pageOptions
|
|
17
|
+
);
|
|
10
18
|
return await deferred(async () => {
|
|
11
19
|
const page = await browser.newPage();
|
|
12
|
-
|
|
20
|
+
console.log('puppeteer task start');
|
|
21
|
+
await page.emulateTimezone(pageOptions.timezone);
|
|
22
|
+
await page.setViewport(Object.assign({}, options.pageViewport, Object.assign({}, { width: options.pageWidth, height: options.pageHeight }, pageOptions.viewport)));
|
|
13
23
|
const output = await (async () => {
|
|
14
24
|
try {
|
|
15
25
|
return {
|
package/libs/services.js
CHANGED
|
@@ -52,33 +52,33 @@ const pdfSchema = {
|
|
|
52
52
|
default: 'A4'
|
|
53
53
|
},
|
|
54
54
|
width: {
|
|
55
|
-
type:
|
|
55
|
+
oneOf: [{ type: 'number' }, { type: 'string' }],
|
|
56
56
|
description: "自定义宽度(如 '8.5in', '200px', 200)"
|
|
57
57
|
},
|
|
58
58
|
height: {
|
|
59
|
-
type:
|
|
59
|
+
oneOf: [{ type: 'number' }, { type: 'string' }],
|
|
60
60
|
description: "自定义高度(如 '11in', '300px', 300)"
|
|
61
61
|
},
|
|
62
62
|
margin: {
|
|
63
63
|
type: 'object',
|
|
64
64
|
properties: {
|
|
65
65
|
top: {
|
|
66
|
-
type:
|
|
66
|
+
oneOf: [{ type: 'number' }, { type: 'string' }],
|
|
67
67
|
description: "上边距(如 '1in', '20px')",
|
|
68
68
|
default: '0.4in'
|
|
69
69
|
},
|
|
70
70
|
right: {
|
|
71
|
-
type:
|
|
71
|
+
oneOf: [{ type: 'number' }, { type: 'string' }],
|
|
72
72
|
description: '右边距',
|
|
73
73
|
default: '0.4in'
|
|
74
74
|
},
|
|
75
75
|
bottom: {
|
|
76
|
-
type:
|
|
76
|
+
oneOf: [{ type: 'number' }, { type: 'string' }],
|
|
77
77
|
description: '下边距',
|
|
78
78
|
default: '0.4in'
|
|
79
79
|
},
|
|
80
80
|
left: {
|
|
81
|
-
type:
|
|
81
|
+
oneOf: [{ type: 'number' }, { type: 'string' }],
|
|
82
82
|
description: '左边距',
|
|
83
83
|
default: '0.4in'
|
|
84
84
|
}
|
|
@@ -109,6 +109,26 @@ const pdfSchema = {
|
|
|
109
109
|
waitForVisible: { type: 'boolean' },
|
|
110
110
|
waitForSelector: {
|
|
111
111
|
oneOf: [{ type: 'array', items: { type: 'string' } }, { type: 'string' }]
|
|
112
|
+
},
|
|
113
|
+
timezone: {
|
|
114
|
+
type: 'string',
|
|
115
|
+
description: '时区',
|
|
116
|
+
default: 'Asia/Shanghai'
|
|
117
|
+
},
|
|
118
|
+
viewport: {
|
|
119
|
+
type: 'object',
|
|
120
|
+
properties: {
|
|
121
|
+
width: {
|
|
122
|
+
type: 'number',
|
|
123
|
+
description: '视口宽度',
|
|
124
|
+
default: 1366
|
|
125
|
+
},
|
|
126
|
+
height: {
|
|
127
|
+
type: 'number',
|
|
128
|
+
description: '视口高度',
|
|
129
|
+
default: 768
|
|
130
|
+
}
|
|
131
|
+
}
|
|
112
132
|
}
|
|
113
133
|
},
|
|
114
134
|
additionalProperties: false
|
|
@@ -155,7 +175,6 @@ const photoSchema = {
|
|
|
155
175
|
description: '裁剪区域高度'
|
|
156
176
|
}
|
|
157
177
|
},
|
|
158
|
-
required: ['x', 'y', 'width', 'height'],
|
|
159
178
|
additionalProperties: false
|
|
160
179
|
},
|
|
161
180
|
omitBackground: {
|
|
@@ -175,6 +194,26 @@ const photoSchema = {
|
|
|
175
194
|
waitForVisible: { type: 'boolean' },
|
|
176
195
|
waitForSelector: {
|
|
177
196
|
oneOf: [{ type: 'array', items: { type: 'string' } }, { type: 'string' }]
|
|
197
|
+
},
|
|
198
|
+
timezone: {
|
|
199
|
+
type: 'string',
|
|
200
|
+
description: '时区',
|
|
201
|
+
default: 'Asia/Shanghai'
|
|
202
|
+
},
|
|
203
|
+
viewport: {
|
|
204
|
+
type: 'object',
|
|
205
|
+
properties: {
|
|
206
|
+
width: {
|
|
207
|
+
type: 'number',
|
|
208
|
+
description: '视口宽度',
|
|
209
|
+
default: 1366
|
|
210
|
+
},
|
|
211
|
+
height: {
|
|
212
|
+
type: 'number',
|
|
213
|
+
description: '视口高度',
|
|
214
|
+
default: 768
|
|
215
|
+
}
|
|
216
|
+
}
|
|
178
217
|
}
|
|
179
218
|
},
|
|
180
219
|
additionalProperties: false
|
|
@@ -219,21 +258,24 @@ module.exports = fp(async (fastify, options) => {
|
|
|
219
258
|
};
|
|
220
259
|
|
|
221
260
|
const parseHtmlToPdf = memoCache('parseHtmlToPdf', async ({ html, options }) => {
|
|
222
|
-
return await puppeteerPage.task(
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
261
|
+
return await puppeteerPage.task(
|
|
262
|
+
async ({ page }) => {
|
|
263
|
+
await page.setContent(html);
|
|
264
|
+
return await page.pdf(
|
|
265
|
+
Object.assign(
|
|
266
|
+
{},
|
|
267
|
+
{
|
|
268
|
+
waitUntil: 'networkidle0',
|
|
269
|
+
displayHeaderFooter: true,
|
|
270
|
+
printBackground: true,
|
|
271
|
+
format: 'A4'
|
|
272
|
+
},
|
|
273
|
+
options
|
|
274
|
+
)
|
|
275
|
+
);
|
|
276
|
+
},
|
|
277
|
+
{ timezone: options?.timezone, viewport: options?.viewport }
|
|
278
|
+
);
|
|
237
279
|
});
|
|
238
280
|
|
|
239
281
|
const parseHtmlToPdfBatch = memoCache('parseHtmlToPdfBatch', async ({ htmlList, options }) => {
|
|
@@ -243,10 +285,13 @@ module.exports = fp(async (fastify, options) => {
|
|
|
243
285
|
});
|
|
244
286
|
|
|
245
287
|
const parseHtmlToPhoto = memoCache('parseHtmlToPhoto', async ({ html, options }) => {
|
|
246
|
-
return await puppeteerPage.task(
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
288
|
+
return await puppeteerPage.task(
|
|
289
|
+
async ({ page }) => {
|
|
290
|
+
await page.setContent(html);
|
|
291
|
+
return await page.screenshot(Object.assign({}, { waitUntil: 'networkidle0', type: 'png' }, options));
|
|
292
|
+
},
|
|
293
|
+
{ timezone: options?.timezone, viewport: options?.viewport }
|
|
294
|
+
);
|
|
250
295
|
});
|
|
251
296
|
|
|
252
297
|
const parseHtmlToPhotoBatch = memoCache('parseHtmlToPhotoBatch', async ({ htmlList, options }) => {
|
|
@@ -256,30 +301,33 @@ module.exports = fp(async (fastify, options) => {
|
|
|
256
301
|
});
|
|
257
302
|
|
|
258
303
|
const parseUrlToPdf = memoCache('parseUrlToPdf', async ({ url, options = {} }) => {
|
|
259
|
-
return await puppeteerPage.task(
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
304
|
+
return await puppeteerPage.task(
|
|
305
|
+
async ({ page }) => {
|
|
306
|
+
await page.goto(url, {
|
|
307
|
+
waitUntil: 'networkidle0'
|
|
308
|
+
});
|
|
309
|
+
await Promise.all(
|
|
310
|
+
(options.waitForSelectors || []).map(waitForSelector => {
|
|
311
|
+
return page.waitForSelector(waitForSelector, {
|
|
312
|
+
visible: options.waitForVisible || false,
|
|
313
|
+
timeout: options.waitForMaxTime || 10000
|
|
314
|
+
});
|
|
315
|
+
})
|
|
316
|
+
);
|
|
317
|
+
return await page.pdf(
|
|
318
|
+
Object.assign(
|
|
319
|
+
{},
|
|
320
|
+
{
|
|
321
|
+
displayHeaderFooter: true,
|
|
322
|
+
printBackground: true,
|
|
323
|
+
format: 'A4'
|
|
324
|
+
},
|
|
325
|
+
options
|
|
326
|
+
)
|
|
327
|
+
);
|
|
328
|
+
},
|
|
329
|
+
{ timezone: options?.timezone, viewport: options?.viewport }
|
|
330
|
+
);
|
|
283
331
|
});
|
|
284
332
|
|
|
285
333
|
const parseUrlToPdfBatch = memoCache('parseUrlToPdfBatch', async ({ urlList, options }) => {
|
|
@@ -289,26 +337,29 @@ module.exports = fp(async (fastify, options) => {
|
|
|
289
337
|
});
|
|
290
338
|
|
|
291
339
|
const parseUrlToPhoto = memoCache('parseUrlToPhoto', async ({ url, selector, options = {} }) => {
|
|
292
|
-
return await puppeteerPage.task(
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
340
|
+
return await puppeteerPage.task(
|
|
341
|
+
async ({ page }) => {
|
|
342
|
+
await page.goto(url, {
|
|
343
|
+
waitUntil: 'networkidle0'
|
|
344
|
+
});
|
|
345
|
+
await Promise.all(
|
|
346
|
+
(options.waitForSelectors || []).map(waitForSelector => {
|
|
347
|
+
return page.waitForSelector(waitForSelector, {
|
|
348
|
+
visible: options.waitForVisible || false,
|
|
349
|
+
timeout: options.waitForMaxTime || 10000
|
|
350
|
+
});
|
|
351
|
+
})
|
|
352
|
+
);
|
|
353
|
+
if (selector) {
|
|
354
|
+
await page.locator(selector).wait();
|
|
355
|
+
const element = await page.$(selector);
|
|
356
|
+
const boundingBox = await element.boundingBox();
|
|
357
|
+
return await page.screenshot(Object.assign({}, { type: 'png' }, options, { clip: boundingBox, fullPage: false }));
|
|
358
|
+
}
|
|
359
|
+
return await page.screenshot(Object.assign({}, { type: 'png' }, options));
|
|
360
|
+
},
|
|
361
|
+
{ timezone: options?.timezone, viewport: options?.viewport }
|
|
362
|
+
);
|
|
312
363
|
});
|
|
313
364
|
|
|
314
365
|
const parseUrlToPhotoBatch = memoCache('parseUrlToPhotoBatch', async ({ urlList, selector, options }) => {
|