HTML5整合本地摄像头,实现视屏预览及拍照上传

WebRTC(Web Real-Time Communication,网页实时通信),是一个支持网页浏览器进行实时语音对话或视频对话的API。

视频流

HTML5 的 The Media Capture(媒体捕捉) API 提供了对摄像头的可编程访问,用户可以直接用 getUserMedia (请注意目前仅Chrome和Opera支持)获得摄像头提供的视频流。我们需要做的是添加一个HTML5 的 Video 标签,并将从摄像头获得的视频作为这个标签的输入来源。

<video id=”video” autoplay=”"></video>


<script>
     var video = document.getElementById('video');

     //访问摄像头
    if (navigator.mediaDevices.getUserMedia || navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia) {
        //调用用户媒体设备, 访问摄像头
        getUserMedia({video: {width: 330, height: 212}}, success, error);
    } else {
        alert('不支持访问用户媒体');
    }
    
     //访问用户媒体设备的兼容方法
    function getUserMedia(constraints, success, error) {
        if (navigator.mediaDevices.getUserMedia) {
            //最新的标准API
            navigator.mediaDevices.getUserMedia(constraints).then(success).catch(error);
        } else if (navigator.webkitGetUserMedia) {
            //webkit核心浏览器
            navigator.webkitGetUserMedia(constraints, success, error)
        } else if (navigator.mozGetUserMedia) {
            //firfox浏览器
            navigator.mozGetUserMedia(constraints, success, error);
        } else if (navigator.getUserMedia) {
            //旧版API
            navigator.getUserMedia(constraints, success, error);
        }
    }

    //成功回调
    function success(stream) {
        //兼容webkit核心浏览器
        var CompatibleURL = window.URL || window.webkitURL;
        //将视频流设置为video元素的源
        // console.log(stream);
        //video.src = CompatibleURL.createObjectURL(stream);
        video.srcObject = stream;
        video.play();
    }

    //失败回调
    function error(error) {
        console.log("访问用户媒体设备失败");
    }

</script>

打开网页,会提示是否允许视屏设备接入,选择允许。此时,video 标签内将显示动态的摄像视频流。

拍照

拍照是采用HTML5的Canvas功能,实时捕获Video标签的内容,因为Video元素可以作为Canvas图像的输入,所以这一点很好实现。主要代码如下:

  //获取图片流
   //动态创建画布对象
  var canvas = document.getElementById("canvas1"); 
  var context = canvas.getContext('2d');
  //将video对象内指定的区域捕捉绘制到画布上指定的区域,可进行不等大不等位的绘制
  context.drawImage(video, 0, 0, 330, 212, 0, 0, 120, 80);
  
  //将Canvas的数据转换为base64位编码的PNG图像
  var imageData = canvas..toDataURL("image/png");
  //截取22位以后的字符串作为图像数据
  var imgStr = imageData.substr(22);
  
  //canvas显示图片(canvas显示上传后的图片用)
  drawCanvasImage(id, src, width, height) {
     var canvas = document.getElementById(id);
     var context = canvas.getContext('2d');
     var img = new Image();
     img.src = src;
     mg.onload = function () {
     context.drawImage(img, 0, 0, width, height);
     };
 }

图片上传

前端使用Ajax将图片上传到服务端。

前端代码
            function uploadImg() {
                //抓取图片并上传图片,返回图片url
                var canvas = document.getElementById("canvas1");
                var canvas1_1 = document.getElementById("canvas1-1");
                var context = canvas.getContext('2d');
                var content1_1 = canvas1_1.getContext('2d');
                context.drawImage(video, 0, 0, 330, 212, 0, 0, 120, 80);
                content1_1.drawImage(video, 0, 0, 330, 212, 0, 0, 720, 480);
                //将Canvas的数据转换为base64位编码的PNG图像
                var imageData = canvas1_1.toDataURL("image/png");
                //截取22位以后的字符串作为图像数据
                var imgStr = imageData.substr(22);
                //Ajax上传图片
                $.ajax({
                    type: 'post',
                    url: '/file/uploadBase64Img',
                    data: {
                        imgStr: imgStr,
                        poundLogNo: poundLogNoStr,
                        uploadType: 1 //上传图片类型 1:称毛重图片 2:称皮重图片
                    },
                    cache: false,
                    dataType: 'json',
                    success: function (result) {
                        // console.log("uploadBase64Img", result);
                        if (result.code == 0) {
                            //图片上传成功
                        } else {
                            layer.alert(result.msg, {icon: 5}); //这时如果你也还想执行yes回调,可以放在第三个参数中。
                        }
                    },
                    error: function (error) {
                        layer.alert("数据请求异常", {icon: 5}); //这时如果你也还想执行yes回调,可以放在第三个参数中。
                    }
                });
            }
后台接口
    /**
     * Base64字符串转图片 上传
     *
     * @param imgStr     base64字符串
     * @param poundLogNo 过磅单号
     * @param uploadType 上传图片类型 1:称毛重图片 2:称皮重图片
     * @return imgUrl
     */
    @RequestMapping(value = "/uploadBase64Img", method = {RequestMethod.POST})
    @ResponseBody
    public Result uploadBase64Img(String imgStr, String poundLogNo, Integer uploadType) {
        if (StringUtils.isEmpty(imgStr)) {
            return ResultUtil.error("图像数据为空");
        }
        if (StringUtil.isEmpty(poundLogNo)) {
            return ResultUtil.error("过磅单号不能为空");
        }
        PoundLog logInDB = poundLogService.findByPoundLogNo(poundLogNo);
        if (logInDB == null) {
            return ResultUtil.error("找不到您要更新的过磅记录");
        }

        //构建图片存储路径
        StringBuilder filePath = new StringBuilder();
        String basePath = DateUtil.getFilePathByDate(systemProperties.getFileLocation() + "/images/upload/");
        filePath.append(basePath).append(poundLogNo).append("/");

        //生成上传图片名
        String fileName = System.currentTimeMillis() + ".jpg";

        //base64字符串转图片并保存
        String imgLocalPath = ImageUtil.saveBase64Img(imgStr, filePath.toString(), fileName);
        if (StringUtils.isEmpty(imgLocalPath)) {
            return ResultUtil.error("文件上传失败");
        }
        //转换成http图片地址
        String imgUrl = systemProperties.getLocalhostUrl() + systemProperties.getServerPort() + imgLocalPath.substring(systemProperties.getFileLocation().length());
        
        if (uploadType == null) {
            return ResultUtil.error("上传图片类型为空");
        }
        if (uploadType == UPLOAD_TYPE_GROSS_IMG) {
            logInDB.setGrossImgUrl(imgUrl);
        } else if (uploadType == UPLOAD_TYPE_TARE_IMG) {
            logInDB.setTareImgUrl(imgUrl);
        } else {
            return ResultUtil.error("未知上传图片类型");
        }

        int result = poundLogService.update(logInDB);
        if (result < 1) {
            return ResultUtil.error("图片保存失败");
        }
        return ResultUtil.success(imgUrl);
    }

效果展示

image.png

:以上的解决方案不仅能用于Web App拍照上传,也可以通过Canvas的编辑功能函数提供图片编辑,例如裁剪、上色、涂鸦、圈点等功能,然后把用户编辑完的图片上传保存到服务器上。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容

  • 在机缘巧合之下,了解到用HTML5和javascript调用摄像头来实现拍照功能,今天就把大致原理写下来。页面布局...
    安易学车阅读 1,252评论 1 0
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,945评论 4 60
  • 你还好意思说,于小忆不是你的好朋友么?你还和我交换条件让我去整她?张帆怒气好像更盛了.奇怪,被整的是我,她生气什么...
    广电2班卢家宁20阅读 138评论 0 0
  • 如果说起“妈妈的味道”不知你会想起什么?是一碗汤面,还是一道小炒,抑或仅仅是一碗白饭。我的妈妈味道特别简...
    紫文阅读 598评论 9 5
  • 总是跟别人说自己是个爱画画的人, 结果一翻自己手里的画都只是草草地开了个头。 也罢,发上来做个鞭策,以期尽快完稿。...
    驚_鴻阅读 253评论 0 3