首页 > 学院 > 开发设计 > 正文

Node.js 通过https服务器利用js调用网络摄像头

2019-11-06 07:17:27
字体:
来源:转载
供稿:网友

前言

最近写了一个js调用摄像头的代码,整体实现是非常简单,但是放到服务器上,通过外网访问后就会出现一个警告 这里写图片描述 我们需要通过js调用摄像头,但在此之前我们需要创建一个https服务器。所以,这篇博文就是帮助大家如何通过js 调用PC端的摄像头。

如何通过js调用摄像头

先看一下js调用摄像头的具体实现。 整个实现是基于Google提供的webRTC技术,它主要用来让浏览器实时获取和交换视频、音频和数据。 WebRTC共分三个API。

MediaStream(又称getUserMedia) RTCPeerConnection RTCDataChannel getUserMedia主要用于获取视频和音频信息,后两个API用于浏览器之间的数据交换。

而我们主要了解MediaStream

下面我们直接看代码:

//获取用户设备媒体对象navigator.getUserMedia({audio:true,video:true},stream => {},err => {});

我们直接通过navigator.getUserMedia方法就可以获取到用户的媒体对象,这个方法有三个参数。 ①第一个参数为一个JSON对象,主要为了设置是否获取音频或视频流对象 ②获取成功的回调函数,这个回调函数有一个stream参数为获取到的流对象 ③出现错误的回调函数,这个回调函数有一个err错误对象

但是现在想让我们的摄像头拍摄到的画面显示出来,我们可以直接通过h5提供的video标签进行加载播放 于是我们可以直接在开头加上video标签,并给它一个id 为rtVideo

<video id="etVideo"></video>

我们不添加它的src属性,而是根据js进行动态赋值。

现在我们开始写获取媒体流成功的回调函数

let onSuccess = stream => { //获取video的dom对象 let rtVideo = document.getElementById("rtVideo"); //为媒体流创建一个url指向 if(window.URL){ webcam.src = window.URL.createObjectURL(stream); } rtVideo.autoplay = true;}

在上述代码中,由于getUserMedia是异步的,因此,通过getUserMedia获取到的媒体流对象后由事件循环取出,将媒体流对象放入获取成功的回调函数的参数,所以上述的stream参数实质就是我们通过getUserMedia获取到的媒体流对象。 然后我们通过getElementById();获取到了video的dom对象

let rtVideo = document.getElementById("rtVideo");

下面的代码我们为获取到的流对象创建了一个URL指向,让它可以被传入vedio的src属性。 window.URL.createObjectURL可以接受一个二进制的流对象作为参数。 随后我们给rtVideo设置上我们创建的url即可

if(window.URL){ rtVideo.src = window.URL.createObjectURL(stream);}

最后我们设置此video为自动播放

rtVideo.autoplay = true;

现在在本地运行上述代码时,我们就可以看见摄像头能够正常调用了。但是注意这只是在本地,而被放置在服务器上,通过外网进行访问时,则会报出开头我贴出的那个警告,告知你调用摄像头必须是一个安全的环境比如https。 下面我们将开始用node.js创建一个https服务器,然后将上述代码通过https服务器返回给客户端。

通过node.js创建https服务器

首先我们得知道https可以看作是一个安全的http服务器,实际上它就是http + ssl/tls协议

创建https服务器必须持有CA机构给出的数字签名才行,这里我们使用openssl进行自签名

step 1:使用openssl创建服务器和客户端的私钥

$ openssl genrsa -out server.key 1024$ openssl genrsa -out client.key 1024

step 2:使用openssl分别创建服务器和客户端的公钥

$ openssl rsa -in server.key -pubout -out server.pem$ openssl rsa -in client.key -pubout -out client.pem

之后我们需要创建一个CA来自签名我们的数字证书

$ openssl genrsa -out ca.key 1024$ openssl req -new -key ca.key -out ca.csr$ openssl x509 req -in ca.csr -signkey ca.key -out ca.crt

随后我们生成服务器的csr数字证书请求文件

$ openssl req -new -key server.key -out server.csr

自签名

$ openssl x509 -req -CA ca.crt -CAkey ca.key -CAcreateserial -in server.csr -out server.crt

到这里我们就已经创建完成一个自签名的服务器数字证书 需要注意的是,ssl/tls 协议是传输层协议,https基于ssl/tls协议,但是在建立起与客户端的链接时,https 还是会执行tcp的三次经典握手环节,这是因为https本质就是一个安全版的http,并且ssl/tls 提供的仅仅是安全加密这一方面的协议,因此,在建立https服务器的时候,其性能会比http慢半拍,因为在其传输的数据的时候,数据会在传输层加密,每一次传输都会有加密解密的环节,至于这半拍是多少,传送门:http://www.ruanyifeng.com/blog/2014/09/ssl-latency.html

经历了tcp握手,接下来就是https的四次握手 ①客户端生成一个base64编码的随机数,然后将这个随机数随同支持的加密方式,ssl/tls协议的版本号一同发送到服务器 ②服务器生成一个base64编码的随机数,将它和 确认的ssl/tls版本发送到客户端,还会发送一个服务器数字证书 ③客户端回应一个编码改变通知和结束握手通知 ④服务器回应一个编码改变通知和结束握手通知

至此,服务器和客户端握手完毕,值得注意的是,上述握手所传输的信息都是明文传输,但是传输的随机数却会根据RSA 来进行加密,此随机数我们称作 PRe master secret ,这个随机数用于生成 session secret ,而session secret是用来利用对称加密来提高加密解密的效率。

至于为什么用三个master secret,这是因为我们的计算机是不会产生真随机数序列,它只会根据一定的算法产生伪随机数序列,一个master secret可能会被猜出来,那三个master secret 就会比较接近随机了。

下面我们利用https 模块来创建一个https服务器,在此之前我需要提一下tls模块,在node中,还封装了一个tls模块,这个模块基于传输层ssl/tls协议的,因此效率比https要高,如同http和tcp 模块。

首先我们引入https模块和fs模块,fs用于读取我们生成的数字证书,私钥,ca的证书等等

//引入模块const fs = require("fs");const https = require("https");

导入服务器的私钥和数字证书 其requestCert 的值为一个Boolean值,当其值为true时,会要求客户端给出相应的证书,默认值为false

//导入服务器的私钥和crt 证书文件let options = { key:fs.readFileSync("./key/server.key"), cert:fs.readFileSync("./key/server.crt"), requestCert:true, ca:[fs.readFileSync("./key/ca.crt")]};

当我们导入options的信息时,我们对请求响应对象的操作与普通http无较大差异

//创建https服务器let httpsServer = https.createServer(options);//监听用户连接httpsServer.on("request",(req,resp) => { console.log("有一用户连接"); resp.setHeader("Content-Type","text/html"); resp.writeHead(200); resp.end(fs.readFileSync("./httpsTest1.html"));});httpsServer.listen(9999,"localhost",() => { console.log("listening");});

至此,我们就创建了一个https服务器,上述代码的httpsTest1.html为文章开头的js代码,用于调用摄像头。 之后我们把代码部署到服务器上,就可以看见成功调用摄像头了,下面贴出完整代码:

node

//引入模块const fs = require("fs");const https = require("https");//导入服务器的私钥和crt 证书文件let options = { key:fs.readFileSync("./key/server.key"), cert:fs.readFileSync("./key/server.crt"), requestCert:true, ca:[fs.readFileSync("./key/ca.crt")]};//创建https服务器let httpsServer = https.createServer(options);//监听用户连接httpsServer.on("request",(req,resp) => { console.log("有一用户连接"); resp.setHeader("Content-Type","text/html"); resp.writeHead(200); resp.end(fs.readFileSync("./httpsTest1.html"));});httpsServer.listen(9999,"localhost",() => { console.log("listening");});

html/js :

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Document</title></head><body> <video id="rtVideo" src=""></video> <script type="text/javascript"> navigator.getUserMedia({audio:true,video:true},stream => { //获取video的dom对象 let rtVideo = document.getElementById("rtVideo"); //为媒体流创建一个url指向 if(window.URL){ rtVideo.src = window.URL.createObjectURL(stream); } rtVideo.autoplay = true; },() => {}); </script></body></html>

在此看来https确实是具有较高安全性。但是其安全性也会随时代的发展而渐渐消弱。

本文有任何问题,请各位读者大大指出。

共勉之


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表