介绍一下用puppeteer的背景,因为公司产品开发需要,在移动端上需要去预览一些网页,并且能把它转换成pdf的模式,正好又接触到了google开源的puppeteer,所以顺势可以写一个node的中间件。
技术选型这块,我用的是koa2来接受移动端的请求,因为网页的配置是需要注入一些cookie进去,不然ajax请求会失败,本身网页 和 iOS 都是我开发的,所以一个人做起来,不用去沟通,比较省事,那么下面进入正题,具体来讲解一下操作过程。
什么是puppeteer?
Puppeteer is a Node library which provides a high-level API to control headless Chrome or Chromium over the DevTools Protocol. It can also be configured to use full (non-headless) Chrome or Chromium.
以上是github的一段介绍,用我的理解就是,你可以不用打开chrome页面,也能来操作网页,并且还附带了一些I/O操作。
基于koa2的配置
我最近是比较喜欢用koa2来做web服务的,大势所趋吧,如果要图方便,可以直接用狼叔的koa脚手架来搞,这个GitHub地址 koa2脚手架,根据教程创好文件之后,安装puppeteer。
npm install puppeteer --save
ok,基本的配置都搞定了,那么接下来就可以撸代码。我贴一点核心的代码,然后配上注释,大家应该就能理解,这个不难,仅需一点node的知识就可以。
准备一个接口,localhost:3000/api/pdf ,这个api接口呢,是接受前端或者移动端的请求,并相应的做出响应的。代码如下。
router.post('/pdf',pdfView.save)
router.prefix('/api') router.post('/pdf',pdfView.save)
在vc里对应的开始写核心方法,引用pupp,引入一个对象存储,为了方便生成好的pdf,我偷懒用了leancloud,把数据上传上去,然后回调的pdf url 传给请求端。
const puppeteer = require('puppeteer');
var AV = require('leancloud-storage');
然后准备开始撸核心代码,下面的是处理事务的核心代码,里面有几个注意事项,首先用的是async awit语法糖 await返回的都是promise,puppeteer的api返回的都是promise,这个你们可以仔细读他的api,一般网站都是需要cookie,因为在http请求的,header总得拼点东西,然后你需要和你的前端沟通好,到底需要传什么东西给你,这样的话也利于调试。
async (ctx,next) => {
let req = ctx.request.body
const browser = await puppeteer.launch();
const page = await browser.newPage();
//放一些你们网页的cookie
page.setCookie({
name: 'token',
value: req.token,
path: '/',
domain: '.xxxx.com',
httpOnly:false,
secure:true,
})
//设置js enabled
await page.setJavaScriptEnabled(true)
//跳转网页
await page.goto(`${这里写你们要的网址}`);
//生成pdf
var pdfData = await page.pdf({path: `${生成名字}.pdf`, format: 'A4'});
//上传pdf数据
var fileResult = await new AV.File(`${req.guid}.pdf`,pdfData).save()
//关闭browser
await browser.close();
//返回pdf 地址
return ctx.body = fileResult.attributes.url
}
接下来,讲最最重要的东西!
如何去判断这个网页加载完成!!!我这里提供一个做法,做一个watchdog,比如用ajax或者axios 请求完数据之后,拼装好网页,让前端去生成一个节点,总之,一定要做一个告诉puppeteer网页已经加载完了!可以去生成pdf了。
$('body').append('<div class="nodeCompleted"></div>')
对应的node 代码是这样的,要写在page.goto()之前。
await page.waitForSelector('.nodeCompleted')
ok,大致的都讲完了,page.pdf() return的是buffer 接下来你可以做一些你们自定义的操作,比如把pdf存到服务器啥的,最后把这个pdf的链接回调给移动端,我再贴一点iOS的代码吧,方便理解,iOS 在线预览pdf 你可以用webview 也能用三方库,我用的是pdf reader。我贴点移动端与node交互的代码 ,仅供参考娱乐下哈。
[EdmHttpsManager POST:@"http://localhost:3100/api/pdf"params:dict ShowSVProgressHUD:YES success:^(idresponse,NSString*result) {
[self addPdfReader:[response mj_JSONString]];
}failure:^(NSError*error) {
}];
等node端回调给你url之后,移动端下载进行保存
- (void)addPdfReader:(NSString*)url{
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL*URL = [NSURLURLWithString:url];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
[EdmHttpsManager showProgressLoadingView];
NSURLSessionDownloadTask*downloadTask = [managerdownloadTaskWithRequest:requestprogress:nildestination:^NSURL*(NSURL*targetPath,NSURLResponse*response) {
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
return[documentsDirectoryURLURLByAppendingPathComponent:[responsesuggestedFilename]];
}completionHandler:^(NSURLResponse*response,NSURL*filePath,NSError*error) {
[EdmHttpsManager hideProgressLoadingView];
ReaderDocument*doc = [[ReaderDocumentalloc]initWithFilePath:filePath.pathpassword:@""];
ReaderViewController *rederVC = [[ReaderViewController alloc] initWithReaderDocument:doc];
rederVC.delegate=self;
rederVC.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
rederVC.modalPresentationStyle = UIModalPresentationOverFullScreen;
[self presentViewController:rederVC animated:YES completion:nil];
}];
[downloadTaskresume];
}
ok,大致的都讲完了,这是我使用puppeteer的一点小小心得,欢迎大家一起交流交流。地址