JS调用摄像头拍照,ajax将图片以base64上传,Java解析base64为图片

本文首先实现js调用摄像头,将视频画面显示在界面上,同时拍照功能(本质为截取当前视频画面),然后将截图通过ajax以base64字符串的形式上传,并在后台将base64转换成图片。

本文主要内容:
一. JS调用摄像头实现拍照并上传springboot后台
二:springboot后台将base64数据转为图片格式并保存,记录常见的错误

一. JS调用摄像头实现拍照并上传springboot后台

  1. 在HTML 页面中添加3个按钮,一个video播放器,一个显示拍照截图的canvas,代码如下:
<button type="button" class="btn btn-primary" onclick="openCamera()">打开摄像头</button>

<button type="button" class="btn btn-success" onclick="snap()">开始&nbsp;拍照</button>

<button type="button" class="btn btn-info" onclick="closeCamera()">关闭摄像头</button>


<video id="video" width="400px" height="400px" autoplay="autoplay"></video>

<canvas id="canvas" width="300px" height="300px"></canvas>
  1. 在js中实现三个按钮的点击函数:
// 视频控件
let video = document.getElementById("video");
let buffer; // 视频流缓冲
/**
 * 开启摄像头,在 视频中
 * @returns {number}
 */
function openCamera() {
    console.log("开启摄像头");
    // 兼容性处理
    navigator.getUserMedia = navigator.getUserMedia ||
        navigator.webkitGetUserMedia ||
        navigator.mozGetUserMedia;
    if (navigator.getUserMedia) {
        navigator.getUserMedia({
                audio: true, video: {width: 256, height: 256}
            }, function (stream) {
                console.log("getUserMedia:", stream)
                buffer = stream;
                //var src = window.URL && window.URL.createObjectURL(buffer) || stream;
                video.muted = true; // 设置视频静音
                video.srcObject = buffer;
                video.onloadedmetadata = function (e) {
                    video.play();
                };
            },
            function (err) {
                alert('哦哦……哪里错了呢?是不是没有摄像头啊?');
                console.log(`哦哦,发生了错误:${err.name}`);
            }
        );
    } else {
        console.log('getUserMedia not supported');
        alert('嗯哼,浏览器不支持 getUserMedia 呢,换最新版火狐浏览器试试!');
    }
    return 0
}

/**
 * 关闭摄像头
 */
function closeCamera() {
    console.log("关闭摄像头");
    buffer && buffer.getVideoTracks()[0].stop(); // 暂停当前播放的音视频
    buffer = null;
}

/**
 * 视频流截图,并传输数据
 */
function snap() {
    console.log('开始拍照');
    if (!buffer) {
        alert('请打开摄像头!');
        console.log('没有视频流');
        return
    }
    let canvas = document.getElementById("canvas");
    let ctx = canvas.getContext('2d');
    ctx.drawImage(video, 0, 0, 256, 256);
    // 将图片数据转换为 base64
    let scan_data = canvas.toDataURL();

    $.ajax({
        type: 'post',
        data: scan_data,
        url: "/snap_data",
        contentType: false,
        processData:false,
        success: function (data) {
            console.log(data.toString())
        },
        error: function (xmlhttprequest) {
            console.log(xmlhttprequest);
        }
    })
}
  1. 如上 function snap() 函数中,实现拍照功能的同时,通过
// 将图片数据转换为 base64
    let scan_data = canvas.toDataURL();

语句可将图片转为base64,再通过ajax传输到后台。
在此处中,我将传输路径定义为 "/snap_data",在后台接收数据格式应为 String

  1. 可手动验证base64数据格式是否正确,通过
    console.log(scan_data);
    打印base64值,再通过在线网站解析base64可看见正确解析为图片

二:springboot后台将base64数据转为图片格式并保存,记录常见的错误

1.新建Controller类,定义路径为 "/snap_data"

  1. base64的数据格式为
data:image/png;base64,iVBORw0KGgoAAAANSUhXXXX...

其中,data:image/png;base64 为头部信息,我们使用Java转码时需要去掉此部分信息,否则会导致图片生成之后无法查看
故,先将base64字符串拆分成头部和内容两部分,其中去掉内容中一些没必要的空格,否则有可能导致图片无法查看

String[] strings = base64string.split(","); // 将base64 头部信息摘出来
System.out.println(strings.length + "\ttype: " + strings[0]);
String imgStr = strings[1].replaceAll(" ",""); // 去掉多余的空格
  1. 获取文件类型函数:
/**
    *
    * @param type
    * @return
    */
   public static String GetBaseImgType(String type) {
       String imgType = BaseImgTypeERR;
       switch (type) {
           case "data:image/jpg;base64":
               imgType = BaseImgTypeJpg;
               break;
           case "data:image/jpeg;base64":
               imgType = BaseImgTypeJpeg;
               break;
           case "data:image/png;base64":
               imgType = BaseImgTypePng;
               break;
           default:break;
       }
       return imgType;
   }
  1. 获取项目绝对路径,并在项目下新建文件夹保存图片
/**
     * 保存图片文件的绝对路径
     *
     * @param fileName
     * @return
     * @throws IOException
     */
    public static File GetLocalIMGPath(String fileName) throws IOException {
        if (null == fileName || fileName.isEmpty()) {
            System.out.println("file name is empty!");
            return null;
        }

        File directory = new File(""); // 定义当前路径
        String path = directory.getCanonicalPath();  // 获取当前路径

        File file_dir = new File(path, "images"); // 文件存放路径
        if (!file_dir.exists()) {
            file_dir.mkdirs();
        }

//        File imageFile = new File(file_dir, fileName).getAbsoluteFile();
//        File local_path = imageFile.getAbsoluteFile();
//        System.out.println(imageFile);

        return new File(file_dir, fileName).getAbsoluteFile();
    }
  1. base64字符串转图片并保存
/**
     * 将 base64 字符串转换成图片
     *
     * @param base64string
     * @param imgPath
     * @return
     * @throws IOException
     */
    public static boolean Base64ToImg(String base64string, File imgPath) throws IOException {
//        图像不为空
        if (null == base64string || base64string.isEmpty())
            return false;
        BASE64Decoder decoder = new BASE64Decoder();
        OutputStream outputStream = null;
        try {
            // 解码
            byte[] bytes = decoder.decodeBuffer(base64string);
            for (int i = 0; i < bytes.length; i++) {
                if (bytes[i] < 0) { // 调整异常数据
                    bytes[i] += 256;
                }
            }
            outputStream = new FileOutputStream(imgPath);
            outputStream.write(bytes);
            outputStream.flush();
            outputStream.close();
            System.out.println(imgPath);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }
  1. Controller中完整调用:
@Controller
public class FileController {

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

推荐阅读更多精彩内容