本文首先实现js调用摄像头,将视频画面显示在界面上,同时拍照功能(本质为截取当前视频画面),然后将截图通过ajax以base64字符串的形式上传,并在后台将base64转换成图片。
本文主要内容:
一. JS调用摄像头实现拍照并上传springboot后台
二:springboot后台将base64数据转为图片格式并保存,记录常见的错误
一. JS调用摄像头实现拍照并上传springboot后台
- 在HTML 页面中添加3个按钮,一个video播放器,一个显示拍照截图的canvas,代码如下:
- 在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);
}
})
}
- 如上 function snap() 函数中,实现拍照功能的同时,通过
// 将图片数据转换为 base64
let scan_data = canvas.toDataURL();
语句可将图片转为base64,再通过ajax传输到后台。
在此处中,我将传输路径定义为 "/snap_data",在后台接收数据格式应为 String
- 可手动验证base64数据格式是否正确,通过
console.log(scan_data);
打印base64值,再通过在线网站解析base64可看见正确解析为图片
二:springboot后台将base64数据转为图片格式并保存,记录常见的错误
1.新建Controller类,定义路径为 "/snap_data"
- 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(" ",""); // 去掉多余的空格
- 获取文件类型函数:
/**
*
* @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;
}
- 获取项目绝对路径,并在项目下新建文件夹保存图片
/**
* 保存图片文件的绝对路径
*
* @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();
}
- 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;
}
}
- 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";
}
}
常见导致图片写入无法查看的错误:
- base64数据没有拆分头部
- base64主体部分没有去掉空格