这两天撸了一个需求,通过 JS  调用手机后置相机,进行拍照扫码。前台实现调用手机相机,然后截取图片并上传到后台的功能。后台接收传过来的图片后,通过调用开源二维码识别库 ZXing 进行二维码数据解析。刚开始在电脑上截图,上传到后台进行识别,测试了几个没有问题。但是发布外网后,一直解析失败。我把手机拍的照片,放到电脑上也是识别不了。后来通过对比图片,发现手机拍的照片有15M大。怀疑是图片过大导致解析二维码失败,才想着把图片无损压缩后再进行二维码识别,压缩后果然见效。

1.图片无损压缩方法

         /// <summary>
/// 无损压缩图片
/// </summary>
/// <param name="sFile">原图片地址</param>
/// <param name="dFile">压缩后保存图片地址</param>
/// <param name="flag">压缩质量(数字越小压缩率越高)1-100</param>
/// <param name="size">压缩后图片的最大大小</param>
/// <param name="sfsc">是否是第一次调用</param>
/// <returns></returns>
public static bool CompressImage(string sFile, string dFile, int flag = , int size = , bool sfsc = true)
{
Image iSource = Image.FromFile(sFile);
ImageFormat tFormat = iSource.RawFormat;
//如果是第一次调用,原始图像的大小小于要压缩的大小,则直接复制文件,并且返回true
FileInfo firstFileInfo = new FileInfo(sFile);
if (sfsc == true && firstFileInfo.Length < size * )
{
firstFileInfo.CopyTo(dFile);
return true;
} int dHeight = iSource.Height / ;
int dWidth = iSource.Width / ;
int sW = , sH = ;
//按比例缩放
Size tem_size = new Size(iSource.Width, iSource.Height);
if (tem_size.Width > dHeight || tem_size.Width > dWidth)
{
if ((tem_size.Width * dHeight) > (tem_size.Width * dWidth))
{
sW = dWidth;
sH = (dWidth * tem_size.Height) / tem_size.Width;
}
else
{
sH = dHeight;
sW = (tem_size.Width * dHeight) / tem_size.Height;
}
}
else
{
sW = tem_size.Width;
sH = tem_size.Height;
} Bitmap ob = new Bitmap(dWidth, dHeight);
Graphics g = Graphics.FromImage(ob); g.Clear(Color.WhiteSmoke);
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; g.DrawImage(iSource, new Rectangle((dWidth - sW) / , (dHeight - sH) / , sW, sH), , , iSource.Width, iSource.Height, GraphicsUnit.Pixel); g.Dispose(); //以下代码为保存图片时,设置压缩质量
EncoderParameters ep = new EncoderParameters();
long[] qy = new long[];
qy[] = flag;//设置压缩的比例1-100
EncoderParameter eParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, qy);
ep.Param[] = eParam; try
{
ImageCodecInfo[] arrayICI = ImageCodecInfo.GetImageEncoders();
ImageCodecInfo jpegICIinfo = null;
for (int x = ; x < arrayICI.Length; x++)
{
if (arrayICI[x].FormatDescription.Equals("JPEG"))
{
jpegICIinfo = arrayICI[x];
break;
}
}
if (jpegICIinfo != null)
{
ob.Save(dFile, jpegICIinfo, ep);//dFile是压缩后的新路径
FileInfo fi = new FileInfo(dFile);
if (fi.Length > * size)
{
flag = flag - ;
CompressImage(sFile, dFile, flag, size, false);
}
}
else
{
ob.Save(dFile, tFormat);
}
return true;
}
catch
{
return false;
}
finally
{ iSource.Dispose();
ob.Dispose();
// System.IO.File.Delete(sFile);
}
}

2.js调用系统相机(推荐Firefox)

 @{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<script src="~/Js/photo/jquery-1.11.3.js"></script>
<script src="~/Js/photo/cropper.min.js"></script>
<link href="~/Js/photo/cropper.min.css" rel="stylesheet" />
<link href="~/Js/photo/ImgCropping.css" rel="stylesheet" />
<script src="~/layer/layer.js"></script>
<style>
/*.cropper-crop-box {
width: 400px !important;
height: 400px !important;
}*/
</style> </head>
<body>
@*<div style="margin-left:50%;margin-top:25%">
<button id="replaceImg" class="l-btn" style="width:400px;height:100px">更换图片</button>
</div>*@ <!--图片裁剪框 start-->
<div style="" class="tailoring-container">
@*<div class="black-cloth" onclick="closeTailor(this)"></div>*@
<div class="tailoring-content">
<div class="tailoring-content-one">
<label title="上传图片" for="chooseImg" class="l2-btn choose-btn">
<input type="file" accept="image/jpg,image/jpeg,image/png" name="file" id="chooseImg" class="hidden" onchange="selectImg(this)">
本地上传
</label>
@*<label title="拍照" class="l2-btn choose-btn" id='capture' style="margin-left: 2%;">拍照</label>
<label title="重拍" class="l2-btn choose-btn" id='takeAgain' style="margin-left: 2%;">重拍</label>*@
@*<div class="close-tailoring" onclick="closeTailor(this)">×</div>*@
</div>
<div class="tailoring-content-two">
<div class="tailoring-box-parcel">
<video id="video" width="80%" height="80%" controls style="float: left;"></video>
<canvas id="canvas" width="482px" height="448px" style="float: left;" hidden="hidden"></canvas>
<div id="showImg" hidden="hidden" style="width: 80%;height:80%;">
<img id="tailoringImg">
</div>
</div>
<div class="preview-box-parcel">
<p>图片预览:</p>
<div class="square previewImg"></div>
@*<div class="circular previewImg"></div>*@
</div>
</div>
<div class="tailoring-content-three">
@*<button class="l2-btn cropper-reset-btn">复位</button>
<button class="l2-btn cropper-rotate-btn">旋转</button>
<button class="l2-btn cropper-scaleX-btn">换向</button>*@
<button class="l2-btn sureCut" id="sureCut">确定</button>
</div>
</div>
</div>
<!--图片裁剪框 end-->
<script> //弹出框水平垂直居中
(window.onresize = function () {
var win_height = $(window).height();
var win_width = $(window).width();
if (win_width <= 768) {
$(".tailoring-content").css({
"top": (win_height - $(".tailoring-content").outerHeight()) / 2,
"left": 0
});
} else {
$(".tailoring-content").css({
"top": (win_height - $(".tailoring-content").outerHeight()) / 2,
"left": (win_width - $(".tailoring-content").outerWidth()) / 2
});
}
})(); //图像上传
function selectImg(file) {
if (!file.files || !file.files[0]) {
return;
}
var reader = new FileReader();
reader.onload = function (evt) {
var replaceSrc = evt.target.result;
//更换cropper的图片
$('#tailoringImg').cropper('replace', replaceSrc, false);//默认false,适应高度,不失真 }
reader.readAsDataURL(file.files[0]);
mediaStreamTrack && mediaStreamTrack.stop();
$("#video").hide();
$("#showImg").show(); }
//cropper图片裁剪
$('#tailoringImg').cropper({
aspectRatio: 1 / 1,//默认比例
preview: '.previewImg',//预览视图
guides: false, //裁剪框的虚线(九宫格)
autoCropArea: 0.5, //0-1之间的数值,定义自动剪裁区域的大小,默认0.8
movable: false, //是否允许移动图片
dragCrop: true, //是否允许移除当前的剪裁框,并通过拖动来新建一个剪裁框区域
movable: true, //是否允许移动剪裁框
resizable: true, //是否允许改变裁剪框的大小
zoomable: false, //是否允许缩放图片大小
mouseWheelZoom: false, //是否允许通过鼠标滚轮来缩放图片
touchDragZoom: true, //是否允许通过触摸移动来缩放图片
rotatable: true, //是否允许旋转图片
crop: function (e) {
// 输出结果数据裁剪图像。
}
}); //弹框
$("#replaceImg").on("click", function () {
takeImg()
}); //旋转
$(".cropper-rotate-btn").on("click", function () {
$('#tailoringImg').cropper("rotate", 45);
});
//复位
$(".cropper-reset-btn").on("click", function () {
$('#tailoringImg').cropper("reset");
});
//换向
var flagX = true;
$(".cropper-scaleX-btn").on("click", function () {
if (flagX) {
$('#tailoringImg').cropper("scaleX", -1);
flagX = false;
} else {
$('#tailoringImg').cropper("scaleX", 1);
flagX = true;
}
flagX != flagX;
}); //裁剪后的处理
var shadIndex = 0;
$("#sureCut").on("click", function () {
var cas = $('#tailoringImg').cropper('getCroppedCanvas');//获取被裁剪后的canvas
var base64url = cas.toDataURL('image/png'); //转换为base64地址形式
base64url = base64url.replace("\r", "")
$.ajax({ url: "/SysPadBindMobile/UploadPhoto",
data: { op: "takePhoto", base64url: base64url },
type: "POST",
dataType: "json",
beforeSend: function () {
shadeIndex = layer.load(2, {
time: 0,
content: '解析中...',
success: function (layero) {
layero.find('.layui-layer-content').css({
'padding-left': '40px',//图标与样式会重合,这样设置可以错开
'width': '200px'//文字显示的宽度
});
}
});
},
success: function (data) {
layer.close(shadeIndex);
//var result = parseInt($.trim(data.result));
if (data.code == -1) {
//未找到绑定列表
var index = parent.layer.getFrameIndex(window.name);
parent.layer.close(index);
window.parent.showAccounts(data.msg); } else if (data.code > 0) {
//存在绑定列表,调用父窗体方法显示
var index = parent.layer.getFrameIndex(window.name);
parent.layer.close(index);
window.parent.showAccounts(data.msg);
//$('#attendance_info').css('color','green').text("已提交");
} else {
// $.messager.alert("失败提示", "头像更新失败,请稍后重试...", 'error')
}
}
});
//关闭裁剪框
closeTailor();
});
//关闭裁剪框
function closeTailor() {
$(".tailoring-container").toggle();
mediaStreamTrack && mediaStreamTrack.stop();
} //访问用户媒体设备的兼容方法
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);
}
} let video = document.getElementById('video');
let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d');
var mediaStreamTrack
function success(stream) {
//兼容webkit核心浏览器
let CompatibleURL = window.URL || window.webkitURL;
//将视频流设置为video元素的源
mediaStreamTrack = stream.getTracks()[0];
//video.src = CompatibleURL.createObjectURL(stream);
video.srcObject = stream;
video.play();
} function error(error) {
alert('访问用户媒体设备失败,请尝试更换浏览器')
} document.getElementById('capture').addEventListener('click', function () {
context.drawImage(video, 0, 0, 480, 320);
mediaStreamTrack && mediaStreamTrack.stop();
$('#tailoringImg').cropper('replace', canvas.toDataURL("image/png"), false);//默认false,适应高度,不失真
$("#video").hide();//隐藏拍照框
$("#showImg").show()//将拍照结果显示
}) //请求拍照
$("#takeAgain").bind("click", function () {
takePhoto();
}); //开始拍照
function takeImg() {
$(".tailoring-container").toggle();
takePhoto();
} //请求摄像头
function takePhoto() {
if (navigator.mediaDevices.getUserMedia || navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia) {
//调用用户媒体设备, 访问摄像头
getUserMedia({ video: { width: 100, height: 100 } }, success, error);
$("#showImg").hide();//隐藏拍照结果显示框
//$('#showImg').html('<img id="tailoringImg" hidden="hidden">')
$("#video").show();//开启拍照框
} else {
alert('不支持访问用户媒体');
}
}
</script> </body> </html>

3.C# 后台接收二维码图片,进行压缩解析的相关代码

         [HttpPost]
public ActionResult UploadPhoto(/*[System.Web.Http.FromBody] photoinfo photo*/)
{
try
{
string base64Str = Request.Form["base64url"];
base64Str = base64Str.Substring(base64Str.IndexOf("base64,") + ); byte[] imgBytes = Convert.FromBase64String(base64Str);
Stream stream = new MemoryStream(imgBytes);
var bit = new Bitmap(stream);
string photoBase = $"{AppDomain.CurrentDomain.BaseDirectory}\\photoimg";
if (!Directory.Exists(photoBase))
{
Directory.CreateDirectory(photoBase);
}
string guid = Guid.NewGuid().ToString();
string oldPath = $"{photoBase}\\{guid}.png";
string newPath = $"{photoBase}\\{guid}_1.png";
bit.Save(oldPath); var compress = CompressImage(oldPath, newPath);
var bindInfo = new QrCodeInfo(); if (!compress)
{
var result = new ZXing.BarcodeReader().Decode(bit);
bit.Dispose();
if (result != null && !string.IsNullOrEmpty(result.Text))
{
bindInfo = Newtonsoft.Json.JsonConvert.DeserializeObject<QrCodeInfo>(result.Text);
}
}
else
{ var f = System.IO.File.Open(newPath, System.IO.FileMode.Open);
var newbit = new Bitmap(f);
var result = new BarcodeReader().Decode(newbit);
f.Dispose();
newbit.Dispose();
if (result != null && !string.IsNullOrEmpty(result.Text))
{
bindInfo = Newtonsoft.Json.JsonConvert.DeserializeObject<QrCodeInfo>(result.Text);
}
}
// bit.Save($"{photoBase}\\{Guid.NewGuid()}.png"); if (bindInfo != null)
{
//传参,调用方法,显示查询查询到的数据
var accountList = _iSysPadBindMobileCore.GetAccountsByMachineId(bindInfo.MachineId, bindInfo.Model);
if (accountList.Any())
{
return Json(new { code = , msg = Newtonsoft.Json.JsonConvert.SerializeObject(accountList) }, JsonRequestBehavior.AllowGet);
}
}
}
catch (Exception ex)
{
return Json(new { code = -, msg = ex.ToString().Substring(, ) }, JsonRequestBehavior.AllowGet);
} return Json(new { code = -, msg = "" }, JsonRequestBehavior.AllowGet);
}

PS:另外在上传图片的过程中,可能会因为图片文件过大导致报500错误,只需要在web.config的<system.web></system.web>节点中配置大小即可:   <httpRuntime maxRequestLength="102400" executionTimeout="200" enable="true" />

C#/.net 通过js调用系统相机进行拍照,图片无损压缩后进行二维码识别的更多相关文章

  1. android调用系统相机并获取图片

    如果不是特别的要求,通过拍照的方式取得图片的话,我们一般调用系统的拍照来完成这项工作,而没必要再自己去实现一个拍照功能.调用系统相机很简单,只需要一个intent就可以跳转到相几界面,然后再通过onA ...

  2. Node.js在指定的图片模板上生成二维码图片并附带底部文字说明

    在Node.js中,我们可以通过qr-image包直接在后台生成二维码图片,使用方法很简单: var qr = require('qr-image'); exports.createQRImage = ...

  3. Android调用系统相机功能

    在常规应用开发过程中,我们经常会使用到手机的相机功能,通过调用系统相机方便快捷的帮助我们实现拍照功能,本篇我将带领大家实现一下,如何通过调用系统相机实现拍照. 第一种:调用系统相机拍照,通过返回的照片 ...

  4. iOS中 读取相册,调用系统相机 技术分享

    技术内容:分别读取相册以及调取相机,将图片显示到imageView上 布局: 1.创建imageView 和 button 并为button一个关联pickerImage的事件 <div sty ...

  5. iOS二维码扫描IOS7系统实现

    扫描相关类 二维码扫描需要获取摄像头并读取照片信息,因此我们需要导入系统的AVFoundation框架,创建视频会话.我们需要用到一下几个类: AVCaptureSession 会话对象.此类作为硬件 ...

  6. H5混合开发二维码扫描以及调用本地摄像头

    今天主管给了我个需求,说要用混合开发,用H5调用本地摄像头进行扫描二维码,我之前有做过原生安卓的二维码扫一扫,主要是通过调用zxing插件进行操作的,其中还弄了个闪光灯.但是纯H5的没接触过,心里没底 ...

  7. html5调用手机本地摄像头和相册识别二维码详细实现过程

    项目中有用到h5识别我们的单据,单据上面有二维码. 实现的场景就是业务人员扫码 类似以下场景  业务员拿到单据以后,直接可以扫码进入相关单据业也可以 输入二维码下方的号码进行识别 下面是h5的页面构造 ...

  8. 修改二维码生成插件jquery.qrcode.js支持加入自定义LOGO

    1,将jquery.qrcode.min.js和jquery添加到您的网页中 <script src="jquery.min.js"></script> & ...

  9. (转)js jquery.qrcode生成二维码 带logo 支持中文

    场景:公司最最近在开发二维码支付业务,所以需要做一个html5中的二维码生成和部署! 前天用js生成二维码,节省服务器资源及带宽 原版jquery.qrcode不能生成logo,本文采用的是修改版 1 ...

随机推荐

  1. P5657 格雷码【民间数据】

    P5657 格雷码[民间数据] 题解 其实这题水啊 打表找规律 [1]0   1 [2]00   01  11  10 [3]000   001   011   010   110   111   1 ...

  2. 《你不知道的JavaScript(上)》笔记——函数作用域和块作用域

    关于函数声明:如果 function 是声明中的第一个词, 那么就是一个函数声明, 否则就是一个函数表达式.例如匿名函数这种形式,函数会被当作函数表达式而不是一个标准的函数声明来处理. (functi ...

  3. 21.Merge Two Sorted Lists 、23. Merge k Sorted Lists

    21.Merge Two Sorted Lists 初始化一个指针作为开头,然后返回这个指针的next class Solution { public: ListNode* mergeTwoLists ...

  4. ubuntu 命令行以及目录的显示改为英文

    在安装ubuntu的时候原本以为把ubuntu改为中文的好理解一些,最后发现命令行的报错一边中文一边英文确实难受,也不宜于学习,所以想把命令行以及目录的显示都改为英文的,我是这样先把命令行的该为英文的 ...

  5. fastjson在将Map<Integer, String>转换成JSON字符串时,出现中文乱码问题

    fastjson在将Map<Integer, String>转换成JSON字符串时,出现中文乱码问题. 先记下这个坑,改天在看看是怎么导致的,暂时通过避免使用Integer作为键(使用St ...

  6. 07Flutter ListView基础列表组件、水平列表组件、图标组件

    ListView:     ListView:参数     scrollDirection:Axis.horizontal:水平列表.Axis.vertical垂直列表     padding:内边距 ...

  7. springboot动态定时任务

    SpringBoot设置动态定时任务 一.说明 1.在我们日常的开发中,很多时候,定时任务都不是写死的,而是写到数据库中,从而实现定时任务的动态配置. 2.定时任务执行时间可根据数据库中某个设置字段动 ...

  8. pyCharm最新2017激活

    pyCharm最新2017:下载地址 下载完成后安装软件 启动pyCharm,进入下面窗口 选择License server 在 server选项里边输入 http://elporfirio.com: ...

  9. Vue的基础学习

    一.Vue的计算属性:get和set属性 <!DOCTYPE html> <html lang="en"> <head> <meta ch ...

  10. mysql访问慢解决

    配置变更思路: 扩大MySQL连接数至2000,同时扩大操作系统最大文件描述符:扩大innodb缓存池 操作步骤: vi /etc/my.cnf max_connections = 2000innod ...