奥门新浦京官方网站JS打开摄像头并截图上传

本文由码农网 –
杨勇原创,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划!

 

2. 如何调用摄像头

$scope.photoErr = false;
$scope.photoBtnDiable = true;
var mediaStream = null,track = null;

navigator.getMedia = (navigator.getUserMedia ||
                      navigator.webkitGetUserMedia || navigator.mozGetUserMedia ||
                      navigator.msGetUserMedia);
        if (navigator.getMedia) {
            navigator.getMedia(
           {
               video: true
           },
           // successCallback
           function (stream) {
               var s = window.URL.createObjectURL(stream);
               var video = document.getElementById('video');
               video.src = window.URL.createObjectURL(stream);
               mediaStream = stream;
               track = stream.getTracks()[0];
               $scope.photoBtnDiable = false;               $scope.$apply();
           },
           // errorCallback
           function (err) {
               $scope.errorPhoto();
               console.log("The following error occured:" + err);
           });
              } else {
            $scope.errorPhoto();
        }

代码解析:

navigator为浏览器对象,包含浏览器的信息,这里就是用这个对象打开摄像头。$scope为AndularJs语法。第一步声明navigator.getMedia来调用浏览器不同的打开摄像头函数,目前仅有getUserMedia、webkitGetUserMedia、mozGetUserMedia、msGetUserMedia四种方式分别对应通用浏览器、Google浏览器、火狐浏览器和IE浏览器,浏览器会自动判断调用哪一个函数。第二步是调用打开浏览器,包含三个参数,分别为需要使用的多媒体类型、获取成功返回的流数据处理函数以及操作失败返回错误消息处理函数。其中,使用时不仅可以设置视频还能设置使用麦克风,设置方式为:

{
      video: true,
      audio: true
}

调用成功即打开摄像头后返回视频流数据,我们可以将流数据设置到video标签在界面上实时显示图像。mediaStream用来记录获取到的流数据,track在Chrome浏览器中用来跟踪摄像头状态,这两个变量都能用来关闭摄像头。

3. 关闭摄像头

// 关闭摄像头
    close.addEventListener('click', function() {
        mediaStreamTrack && mediaStreamTrack.stop();
    }, false);

 

奥门新浦京官方网站,4. 如何关闭摄像头

$scope.closeCamera = function () {
            if (mediaStream != null) {
                if (mediaStream.stop) {
                    mediaStream.stop();
                }
                $scope.videosrc = "";
            }
            if (track != null) {
                if (track.stop) {
                    track.stop();
                }
            }
        }

正如前面所说,关闭摄像头的方式是通过mediaStream和track变量,只不过,track只能关闭Chrome浏览器中的摄像头,这也是Chrome 45版本以上关闭摄像头的方式。

奥门新浦京官方网站 1

1. 项目背景

公司开发一个网站,在做用户头像修改的时候领导提到增加一个由摄像头拍照实现修改头像的功能。因为我们网站是基于Html5进行开发,所以就直接采用H5来实现拍照。起初觉得这个功能很简单,但是做的时候才发现并不是那么简单的。

奥门新浦京官方网站 2

这是在AngularJs中成功实现调用摄像头拍照并截图上传的例图:

奥门新浦京官方网站 3

奥门新浦京官方网站 4

4. 上传截取的图像

canvas.toDataURL(‘image/png’)

// 上传截取的图像
    upload.addEventListener('click', function() {
        jQuery.post('/uploadSnap.php', {
            snapData: canvas.toDataURL('image/png')
        }).done(function(rs) {
            rs = JSON.parse(rs);

            console.log(rs);

            uploaded.src = rs.path;
        }).fail(function(err) {
            console.log(err);
        });
    }, false);

而这里的后端(PHP)则将获取的内容转换成图像文件保存

需要注意的是,要将base64的头部信息字段去掉再保存,否则似乎图像是损坏无法打开滴

<?php

    $snapData = $_POST['snapData'];
    $snapData = str_replace('data:image/png;base64,', '', $snapData);
    // $snapData = str_replace(' ', '+', $snapData);

    $img = base64_decode($snapData);

    $uploadDir = 'upload/';
    $fileName = date('YmdHis', time()) . uniqid();

    if (!(file_put_contents($uploadDir . $fileName, $img))) {
        echo json_encode(array('code' => 500, 'msg' => '文件上传失败'));
    } else {
        echo json_encode(array('code' => 200, 'msg' => '文件上传成功', 'path' => $uploadDir . $fileName));
    }

?>

 

完整JS代码

奥门新浦京官方网站 5奥门新浦京官方网站 6

 1 <script type="text/javascript" src="jquery.js"></script>
 2     <script type="text/javascript">
 3     function $(elem) {
 4         return document.querySelector(elem);
 5     }
 6 
 7     // 获取媒体方法(旧方法)
 8     navigator.getMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMeddia || navigator.msGetUserMedia;
 9 
10     var canvas = $('canvas'),
11         context = canvas.getContext('2d'),
12         video = $('video'),
13         snap = $('#snap'),
14         close = $('#close'),
15         upload = $('#upload'),
16         uploaded = $('#uploaded'),
17         mediaStreamTrack;
18 
19     // 获取媒体方法(新方法)
20     // 使用新方法打开摄像头
21     if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
22         navigator.mediaDevices.getUserMedia({
23             video: true,
24             audio: true
25         }).then(function(stream) {
26             console.log(stream);
27 
28             mediaStreamTrack = typeof stream.stop === 'function' ? stream : stream.getTracks()[1];
29 
30             video.src = (window.URL || window.webkitURL).createObjectURL(stream);
31             video.play();
32         }).catch(function(err) {
33             console.log(err);
34         })
35     }
36     // 使用旧方法打开摄像头
37     else if (navigator.getMedia) {
38         navigator.getMedia({
39             video: true
40         }, function(stream) {
41             mediaStreamTrack = stream.getTracks()[0];
42 
43             video.src = (window.URL || window.webkitURL).createObjectURL(stream);
44             video.play();
45         }, function(err) {
46             console.log(err);
47         });
48     }
49 
50     // 截取图像
51     snap.addEventListener('click', function() {
52         context.drawImage(video, 0, 0, 200, 150);
53     }, false);
54 
55     // 关闭摄像头
56     close.addEventListener('click', function() {
57         mediaStreamTrack && mediaStreamTrack.stop();
58     }, false);
59 
60     // 上传截取的图像
61     upload.addEventListener('click', function() {
62         jQuery.post('/uploadSnap.php', {
63             snapData: canvas.toDataURL('image/png')
64         }).done(function(rs) {
65             rs = JSON.parse(rs);
66 
67             console.log(rs);
68 
69             uploaded.src = rs.path;
70         }).fail(function(err) {
71             console.log(err);
72         });
73     }, false);
74 
75     </script>

View Code

 

3. 拍照

$scope.snap = function () {
        var canvas = document.createElement('canvas');
            canvas.width = "400";
            canvas.height = "304";

            var ctx = canvas.getContext('2d');
            ctx.drawImage(video, 0, 0, 400, 304);
            $scope.closeCamera();
            $uibModalInstance.close(canvas.toDataURL("image/png"));
};

拍照时需要使用到canvas标签,创建一个canvas标签,设置我们需要拍照的尺寸大小,通过drawImage函数将video当前的图像保存到canvas标签,最后将图像数据转换为base64数据返回并关闭摄像头,这样就完成了我们的拍照功能。这里的$uibModalInstance对象是我们项目中打开弹出层的一个对象,用来控制弹出层的显示。

奥门新浦京官方网站 7

5. 集成到AndularJs

事实上,前面所说的都是在AndularJs中实现的,当然,这里只是实现了拍照并返回拍照数据,我们想要在其他地方也使用,就需要将这部分独立出来,这里我们用到了AngularJs中的service机制,将这部分单独做成一个service并在项目中注入,然后就可以在其他地方调用了。

service注册:

app().registerService("h5TakePhotoService", function ($q, $uibModal) {

        this.photo = function () {
            var deferred = $q.defer();
            require([config.server + "/com/controllers/photo.js"], function () {
                $uibModal.open({
                    templateUrl: config.server + "/com/views/modal_take_photo.html",
                    controller: "photoModalController",
                    windowClass: "modal-photo"
                }).result.then(function (e) {
                    deferred.resolve(e);
                });
            });
            return deferred.promise;
        }

    });

调用方式:

$scope.takePhoto = function () {
      h5TakePhotoService.photo().then(function (res) {
           if (res != null && res != "") {
               $scope.myImage = res;
           }
      });
}

h5TakePhotoService为控制器中注入的拍照service对象,最后处理返回的图像数据,设置数据显示到界面上。

 

6. 兼容问题

主要存在Chrome浏览器中,本地测试时,Chrome浏览器中能够正常使用,但是部署到服务器后就不能正常使用,报错消息为 [object NavigatorUserMediaError],这是因为Chrome浏览器在使用摄像头时只支持安全源访问,所以只能通过https访问才能正常使用。

最后需要说一下,测试时只能通过 Studio、 java web、php中完成。

 

1.
打开摄像头主要用到getUserMedia方法,然后将获取到的媒体流置入video标签

2.
截取图片主要用到canvas绘图,使用drawImage方法将video的内容绘至canvas中

3.
将截取的内容上传至服务器,将canvas中的内容转为base64格式上传,后端(PHP)通过file_put_contents将其转为图片

直入正题,JS打开摄像头并截图上传至后端的一个完整步骤

比如在最新版FireFox中的报错,不知为啥

奥门新浦京官方网站 8

要注意的是,在chrome以外的浏览器中,使用摄像头或多或少会出现一些问题,可能也是老问题了,所以以下代码主要基于chrome使用

2. 截取图像

将内容写入即可

// 截取图像
    snap.addEventListener('click', function() {
        context.drawImage(video, 0, 0, 200, 150);
    }, false);

 

1. 打开摄像头

getUserMedia 有新版本和旧版本两种,建议使用新版本

 

旧版本位于navigator 对象下,根据浏览器不同有所不同

// 获取媒体方法(旧方法)
    navigator.getMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMeddia || navigator.msGetUserMedia;

if (navigator.getMedia) {
        navigator.getMedia({
            video: true
        }, function(stream) {
            mediaStreamTrack = stream.getTracks()[0];

            video.src = (window.URL || window.webkitURL).createObjectURL(stream);
            video.play();
        }, function(err) {
            console.log(err);
        });
    }

第一个参数中指示需要使用视频(video)或音频(audio),更多参见文档

第二个参数中指示调用成功后的回调,其中带一个参数(MediaStream),在旧版本中可以直接通过调用MediaStream.stop() 来关闭摄像头,不过在新版之中已废弃。需要使用MediaStream.getTracks()[index].stop() 来关闭相应的Track

第三个参数指示调用失败后的回调

 

新版本位于navigator.mediaDevices 对象下

if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
        navigator.mediaDevices.getUserMedia({
            video: true,
            audio: true
        }).then(function(stream) {
            console.log(stream);

            mediaStreamTrack = typeof stream.stop === 'function' ? stream : stream.getTracks()[1];

            video.src = (window.URL || window.webkitURL).createObjectURL(stream);
            video.play();
        }).catch(function(err) {
            console.log(err);
        })
    }

与旧版类似,不过该方法返回了一个Promise对象,可以使用then和catch表示成功与失败的回调

更多参见文档

需要注意的是,MediaStream.getTracks() 返回的Tracks数组是按第一个参数倒序排列的

比如现在定义了

{
    video: true,
    audio: true
}

想关闭摄像头,就需要调用MediaStream.getTracks()[1].stop();

同理,0对应于audio的track

 

使用createObjectURL 将MediaStream写入video标签,就能够存储实时的媒体流数据(也可以方便的实时查看画面)

旧版本中webkitURL 对象以不被支持,需要使用URL对象

 

  <video width="200" height="150"></video>
    <canvas width="200" height="150"></canvas>

    <p>
        <button id="snap">截取图像</button>
        <button id="close">关闭摄像头</button>
        <button id="upload">上传图像</button>
    </p>

    <img id="uploaded" width="200" height="150" />

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注