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

2021-07-14  本文已影响0人  天降小纸箱

本文首先实现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主体部分没有去掉空格
上一篇下一篇

猜你喜欢

热点阅读