nginx实现图片上传+puppeteer截取网页图片

需求: 有两个网络环境A,B,需要从B的内网服务中每天定时截取业务信息以便在手机上随时查看。
场景:B没有外网ip。A有外网ip,A没有图片存储服务。
成本最低的方案:A部署一个nginx图片上传的服务。B中使用puppeteer+node访问内网实现截屏,并且调用A的接口上传图片,这样就可以通过A的外网ip查看图片

一,配置A的nginx

A原本部署了一个react项目,针对所有的请求做了拦截操作。只需要在react的location 拦截之前做一下精确匹配即可。
上传图片需要使用nginx upload module 官方文档:http://www.grid.net.ru/nginx/upload.en.html。

[关于一些对location认识的误区]

1、 location 的匹配顺序是“先匹配正则,再匹配普通”。

矫正: location 的匹配顺序其实是“先匹配普通,再匹配正则”。我这么说,大家一定会反驳我,因为按“先匹配普通,再匹配正则”解释不了大家平时习惯的按“先匹配正则,再匹配普通”的实践经验。这里我只能暂时解释下,造成这种误解的原因是:正则匹配会覆盖普通匹配(实际的规则,比这复杂,后面会详细解释)。

2、 location 的执行逻辑跟 location 的编辑顺序无关。

矫正:这句话不全对,“普通 location ”的匹配规则是“最大前缀”,因此“普通 location ”的确与 location 编辑顺序无关;但是“正则 location ”的匹配规则是“顺序匹配,且只要匹配到第一个就停止后面的匹配”;“普通location ”与“正则 location ”之间的匹配顺序是?先匹配普通 location ,再“考虑”匹配正则 location 。注意这里的“考虑”是“可能”的意思,也就是说匹配完“普通 location ”后,有的时候需要继续匹配“正则 location ”,有的时候则不需要继续匹配“正则 location ”。两种情况下,不需要继续匹配正则 location :( 1 )当普通 location 前面指定了“ ^~ ”,特别告诉 Nginx 本条普通 location 一旦匹配上,则不需要继续正则匹配;( 2 )当普通location 恰好严格匹配上,不是最大前缀匹配,则不再继续匹配正则。

总结一句话: “正则 location 匹配让步普通 location 的严格精确匹配结果;但覆盖普通 location 的最大前缀匹配结果”
参见:## 关于一些对location认识的误区
nginx配置

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
	# 开启gzip
    gzip on;
    # 启用gzip压缩的最小文件,小于设置值的文件将不会压缩
    gzip_min_length 1k;
    # gzip 压缩级别,1-9,数字越大压缩的越好,也越占用CPU时间,后面会有详细说明
    gzip_comp_level 4;
    # 进行压缩的文件类型。javascript有多种形式。其中的值可以在 mime.types 文件中找到。
    gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png application/vnd.ms-fontobject font/ttf font/opentype font/x-woff image/svg+xml;
    # 是否在http header中添加Vary: Accept-Encoding,建议开启
    gzip_vary on;
    # 禁用IE 6 gzip
    gzip_disable "MSIE [1-6]\.";
    # 设置压缩所需要的缓冲区大小     
    gzip_buffers 32 4k;
    # 设置gzip压缩针对的HTTP协议版本
    gzip_http_version 1.0;
    server {
        listen       80;
        listen 443 ssl;
        ssl on;
        ssl_certificate  ###;
        ssl_certificate_key ###;
        ssl_session_timeout 5m;
        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;
        server_name  www.########;
	root	/data/program/build/;
	index	task.html;
	  location ^~ /download {
            autoindex on;
    	    autoindex_exact_size on;
    	    autoindex_localtime on;
	        alias /tmp/upload_tmp; 
        }
        #只要匹配到文件上传路径就直接不在进行其他匹配
	 location ^~ /upload/ {
	        upload_pass /res_upload;
            upload_store /tmp/upload_tmp;
            upload_store_access user:rw;
            upload_limit_rate 0;
			upload_set_form_field "${upload_field_name}_name" $upload_file_name;
            upload_set_form_field "content_type" $upload_content_type;
            upload_set_form_field "tmp_path" $upload_tmp_path;
            upload_aggregate_form_field "md5" $upload_file_md5;
            upload_aggregate_form_field "size" $upload_file_size;
            upload_pass_form_field "^.*$";
            
	    }	
	location / {
        try_files $uri @fallback;
    }

    location @fallback {
        rewrite .* /task.html break;
    }
    error_page 404 /404.html;
        location = /40x.html {
    }
    error_page 500 502 503 504 /50x.html;
        location = /50x.html {
    }
	
	}
	
}

二 使用puppeteer+Chrome引擎+node实现网页截屏。
Puppeteer是谷歌官方出品的一个通过DevTools协议控制headless Chrome的Node库。可以通过Puppeteer的提供的api直接控制Chrome模拟大部分用户操作来进行UI Test或者作为爬虫访问页面来收集数据。当然可以用来截屏。
代码

const puppeteer = require('puppeteer');
var https = require('https');
var FormData = require('form-data');
var request = require('request');
var fs = require('fs');
const path="/opt/google/chrome/chrome";
const filePath = "/usr/local/screenShot/filePath/1.png";
//const path="C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe";
//const filePath = "C:\\data\\images\\1.png";
(async () => {
  const browser = await puppeteer.launch({
    headless: true,executablePath: path,args:['--no-sandbox']
  });
  const page = await browser.newPage();
  //注入session 免登录
  await page.evaluateOnNewDocument(function() {
    sessionStorage.setItem('__token', '###');
    sessionStorage.setItem('userInfo','{"token":"###");
  });
  await page.goto('http://*****/');
  await page.setViewport({
    width: 1500,
    height: 800
  });
//截屏操作
  setTimeout(async function () {
    await autoScroll(page);
    try {
      await page.screenshot({
        fullPage: true,
        path: filePath,
        //path:"/usr/local/screenShot/1.png"
      });
    } catch (e) {
      console.log("taskLogInfo:screenShotFailed");
     console.log(e);
    } finally {
      console.log("taskLogInfo:systemOut");
      //await process.exit();
      setTimeout(async function () {
        try {
          //发送图片到A服务器
          const formData = {
            custom_file: {
              value:  fs.createReadStream(filePath),
              options: {
                filename: '1.png',
                contentType: 'multipart/form-data; boundary=----WebKitFormBoundaryGrUwVzlG9ou8k1If'
              }
            }
          };
          request.post({url:'https://####/', formData: formData}, function optionalCallback(err, httpResponse, body) {
            if (err) {
              return console.error('upload failed:', err);
            }
            console.log('Upload successful!  Server responded with:', body);
          });
        } catch (e) {
          console.log("sendfile failed");
          console.log(e);
        }finally{
        //关闭浏览器
          await browser.close();
        }
      }, 5 * 1000);
    }

  }, 20 * 1000);
})();

//网页滚动加载
function autoScroll(page) {
  return page.evaluate(() => {
    return new Promise((resolve, reject) => {
      var totalHeight = 0;
      var distance = 100;
      var timer = setInterval(() => {
        var scrollHeight = document.body.scrollHeight;
        window.scrollBy(0, distance);
        totalHeight += distance;
        if (totalHeight >= scrollHeight) {
          clearInterval(timer);
          resolve();
        }
      }, 100);
    })
  });
}

总结:简简单单完成需求

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值