C#/.net 通過js調用系統相機進行拍照,圖片無損壓縮后進行二維碼識別


這兩天擼了一個需求,通過 JS  調用手機后置相機,進行拍照掃碼。前台實現調用手機相機,然后截取圖片並上傳到后台的功能。后台接收傳過來的圖片后,通過調用開源二維碼識別庫 ZXing 進行二維碼數據解析。剛開始在電腦上截圖,上傳到后台進行識別,測試了幾個沒有問題。但是發布外網后,一直解析失敗。我把手機拍的照片,放到電腦上也是識別不了。后來通過對比圖片,發現手機拍的照片有15M大。懷疑是圖片過大導致解析二維碼失敗,才想着把圖片無損壓縮后再進行二維碼識別,壓縮后果然見效。

1.圖片無損壓縮方法

  1         /// <summary>
  2         /// 無損壓縮圖片
  3         /// </summary>
  4         /// <param name="sFile">原圖片地址</param>
  5         /// <param name="dFile">壓縮后保存圖片地址</param>
  6         /// <param name="flag">壓縮質量(數字越小壓縮率越高)1-100</param>
  7         /// <param name="size">壓縮后圖片的最大大小</param>
  8         /// <param name="sfsc">是否是第一次調用</param>
  9         /// <returns></returns>
 10         public static bool CompressImage(string sFile, string dFile, int flag = 90, int size = 300, bool sfsc = true)
 11         {
 12             Image iSource = Image.FromFile(sFile);
 13             ImageFormat tFormat = iSource.RawFormat;
 14             //如果是第一次調用,原始圖像的大小小於要壓縮的大小,則直接復制文件,並且返回true
 15             FileInfo firstFileInfo = new FileInfo(sFile);
 16             if (sfsc == true && firstFileInfo.Length < size * 1024)
 17             {
 18                 firstFileInfo.CopyTo(dFile);
 19                 return true;
 20             }
 21 
 22             int dHeight = iSource.Height / 2;
 23             int dWidth = iSource.Width / 2;
 24             int sW = 0, sH = 0;
 25             //按比例縮放
 26             Size tem_size = new Size(iSource.Width, iSource.Height);
 27             if (tem_size.Width > dHeight || tem_size.Width > dWidth)
 28             {
 29                 if ((tem_size.Width * dHeight) > (tem_size.Width * dWidth))
 30                 {
 31                     sW = dWidth;
 32                     sH = (dWidth * tem_size.Height) / tem_size.Width;
 33                 }
 34                 else
 35                 {
 36                     sH = dHeight;
 37                     sW = (tem_size.Width * dHeight) / tem_size.Height;
 38                 }
 39             }
 40             else
 41             {
 42                 sW = tem_size.Width;
 43                 sH = tem_size.Height;
 44             }
 45 
 46             Bitmap ob = new Bitmap(dWidth, dHeight);
 47             Graphics g = Graphics.FromImage(ob);
 48 
 49             g.Clear(Color.WhiteSmoke);
 50             g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
 51             g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
 52             g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
 53 
 54             g.DrawImage(iSource, new Rectangle((dWidth - sW) / 2, (dHeight - sH) / 2, sW, sH), 0, 0, iSource.Width, iSource.Height, GraphicsUnit.Pixel);
 55 
 56             g.Dispose();
 57 
 58             //以下代碼為保存圖片時,設置壓縮質量
 59             EncoderParameters ep = new EncoderParameters();
 60             long[] qy = new long[1];
 61             qy[0] = flag;//設置壓縮的比例1-100
 62             EncoderParameter eParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, qy);
 63             ep.Param[0] = eParam;
 64 
 65             try
 66             {
 67                 ImageCodecInfo[] arrayICI = ImageCodecInfo.GetImageEncoders();
 68                 ImageCodecInfo jpegICIinfo = null;
 69                 for (int x = 0; x < arrayICI.Length; x++)
 70                 {
 71                     if (arrayICI[x].FormatDescription.Equals("JPEG"))
 72                     {
 73                         jpegICIinfo = arrayICI[x];
 74                         break;
 75                     }
 76                 }
 77                 if (jpegICIinfo != null)
 78                 {
 79                     ob.Save(dFile, jpegICIinfo, ep);//dFile是壓縮后的新路徑
 80                     FileInfo fi = new FileInfo(dFile);
 81                     if (fi.Length > 1024 * size)
 82                     {
 83                         flag = flag - 10;
 84                         CompressImage(sFile, dFile, flag, size, false);
 85                     }
 86                 }
 87                 else
 88                 {
 89                     ob.Save(dFile, tFormat);
 90                 }
 91                 return true;
 92             }
 93             catch
 94             {
 95                 return false;
 96             }
 97             finally
 98             {
 99 
100                 iSource.Dispose();
101                 ob.Dispose();
102                // System.IO.File.Delete(sFile);
103             }
104         }

2.js調用系統相機(推薦Firefox)

  1 @{
  2     Layout = null;
  3 }
  4 <!DOCTYPE html>
  5 <html>
  6 <head>
  7     <script src="~/Js/photo/jquery-1.11.3.js"></script>
  8     <script src="~/Js/photo/cropper.min.js"></script>
  9     <link href="~/Js/photo/cropper.min.css" rel="stylesheet" />
 10     <link href="~/Js/photo/ImgCropping.css" rel="stylesheet" />
 11     <script src="~/layer/layer.js"></script>
 12     <style>
 13         /*.cropper-crop-box {
 14             width: 400px !important;
 15             height: 400px !important;
 16         }*/
 17     </style>
 18 
 19 </head>
 20 <body>
 21     @*<div style="margin-left:50%;margin-top:25%">
 22             <button id="replaceImg" class="l-btn" style="width:400px;height:100px">更換圖片</button>
 23         </div>*@
 24 
 25     <!--圖片裁剪框 start-->
 26     <div style="" class="tailoring-container">
 27         @*<div class="black-cloth" onclick="closeTailor(this)"></div>*@
 28         <div class="tailoring-content">
 29             <div class="tailoring-content-one">
 30                 <label title="上傳圖片" for="chooseImg" class="l2-btn choose-btn">
 31                     <input type="file" accept="image/jpg,image/jpeg,image/png" name="file" id="chooseImg" class="hidden" onchange="selectImg(this)">
 32                     本地上傳
 33                 </label>
 34                 @*<label title="拍照" class="l2-btn choose-btn" id='capture' style="margin-left: 2%;">拍照</label>
 35                     <label title="重拍" class="l2-btn choose-btn" id='takeAgain' style="margin-left: 2%;">重拍</label>*@
 36                 @*<div class="close-tailoring" onclick="closeTailor(this)">×</div>*@
 37             </div>
 38             <div class="tailoring-content-two">
 39                 <div class="tailoring-box-parcel">
 40                     <video id="video" width="80%" height="80%" controls style="float: left;"></video>
 41                     <canvas id="canvas" width="482px" height="448px" style="float: left;" hidden="hidden"></canvas>
 42                     <div id="showImg" hidden="hidden" style="width: 80%;height:80%;">
 43                         <img id="tailoringImg">
 44                     </div>
 45                 </div>
 46                 <div class="preview-box-parcel">
 47                     <p>圖片預覽:</p>
 48                     <div class="square previewImg"></div>
 49                     @*<div class="circular previewImg"></div>*@
 50                 </div>
 51             </div>
 52             <div class="tailoring-content-three">
 53                 @*<button class="l2-btn cropper-reset-btn">復位</button>
 54                     <button class="l2-btn cropper-rotate-btn">旋轉</button>
 55                     <button class="l2-btn cropper-scaleX-btn">換向</button>*@
 56                 <button class="l2-btn sureCut" id="sureCut">確定</button>
 57             </div>
 58         </div>
 59     </div>
 60     <!--圖片裁剪框 end-->
 61     <script>
 62 
 63         //彈出框水平垂直居中
 64         (window.onresize = function () {
 65             var win_height = $(window).height();
 66             var win_width = $(window).width();
 67             if (win_width <= 768) {
 68                 $(".tailoring-content").css({
 69                     "top": (win_height - $(".tailoring-content").outerHeight()) / 2,
 70                     "left": 0
 71                 });
 72             } else {
 73                 $(".tailoring-content").css({
 74                     "top": (win_height - $(".tailoring-content").outerHeight()) / 2,
 75                     "left": (win_width - $(".tailoring-content").outerWidth()) / 2
 76                 });
 77             }
 78         })();
 79 
 80 
 81         //圖像上傳
 82         function selectImg(file) {
 83             if (!file.files || !file.files[0]) {
 84                 return;
 85             }
 86             var reader = new FileReader();
 87             reader.onload = function (evt) {
 88                 var replaceSrc = evt.target.result;
 89                 //更換cropper的圖片
 90                 $('#tailoringImg').cropper('replace', replaceSrc, false);//默認false,適應高度,不失真
 91 
 92             }
 93             reader.readAsDataURL(file.files[0]);
 94             mediaStreamTrack && mediaStreamTrack.stop();
 95             $("#video").hide();
 96             $("#showImg").show();
 97 
 98         }
 99         //cropper圖片裁剪
100         $('#tailoringImg').cropper({
101             aspectRatio: 1 / 1,//默認比例
102             preview: '.previewImg',//預覽視圖
103             guides: false,  //裁剪框的虛線(九宮格)
104             autoCropArea: 0.5,  //0-1之間的數值,定義自動剪裁區域的大小,默認0.8
105             movable: false, //是否允許移動圖片
106             dragCrop: true,  //是否允許移除當前的剪裁框,並通過拖動來新建一個剪裁框區域
107             movable: true,  //是否允許移動剪裁框
108             resizable: true,  //是否允許改變裁剪框的大小
109             zoomable: false,  //是否允許縮放圖片大小
110             mouseWheelZoom: false,  //是否允許通過鼠標滾輪來縮放圖片
111             touchDragZoom: true,  //是否允許通過觸摸移動來縮放圖片
112             rotatable: true,  //是否允許旋轉圖片
113             crop: function (e) {
114                 // 輸出結果數據裁剪圖像。
115             }
116         });
117 
118         //彈框
119         $("#replaceImg").on("click", function () {
120             takeImg()
121         });
122 
123         //旋轉
124         $(".cropper-rotate-btn").on("click", function () {
125             $('#tailoringImg').cropper("rotate", 45);
126         });
127         //復位
128         $(".cropper-reset-btn").on("click", function () {
129             $('#tailoringImg').cropper("reset");
130         });
131         //換向
132         var flagX = true;
133         $(".cropper-scaleX-btn").on("click", function () {
134             if (flagX) {
135                 $('#tailoringImg').cropper("scaleX", -1);
136                 flagX = false;
137             } else {
138                 $('#tailoringImg').cropper("scaleX", 1);
139                 flagX = true;
140             }
141             flagX != flagX;
142         });
143 
144         //裁剪后的處理
145         var shadIndex = 0;
146         $("#sureCut").on("click", function () {
147             var cas = $('#tailoringImg').cropper('getCroppedCanvas');//獲取被裁剪后的canvas
148             var base64url = cas.toDataURL('image/png'); //轉換為base64地址形式
149             base64url = base64url.replace("\r", "")
150             $.ajax({
151 
152                 url: "/SysPadBindMobile/UploadPhoto",
153                 data: { op: "takePhoto", base64url: base64url },
154                 type: "POST",
155                 dataType: "json",
156                 beforeSend: function () {
157                     shadeIndex = layer.load(2, {
158                         time: 0,
159                         content: '解析中...',
160                         success: function (layero) {
161                             layero.find('.layui-layer-content').css({
162                                 'padding-left': '40px',//圖標與樣式會重合,這樣設置可以錯開
163                                 'width': '200px'//文字顯示的寬度
164                             });
165                         }
166                     });
167                 },
168                 success: function (data) {
169                     layer.close(shadeIndex);
170                     //var result = parseInt($.trim(data.result));
171                     if (data.code == -1) {
172                         //未找到綁定列表
173                         var index = parent.layer.getFrameIndex(window.name);
174                         parent.layer.close(index);
175                         window.parent.showAccounts(data.msg);
176 
177                     } else if (data.code > 0) {
178                         //存在綁定列表,調用父窗體方法顯示
179                         var index = parent.layer.getFrameIndex(window.name);
180                         parent.layer.close(index);
181                         window.parent.showAccounts(data.msg);
182                         //$('#attendance_info').css('color','green').text("已提交");
183                     } else {
184                         //  $.messager.alert("失敗提示", "頭像更新失敗,請稍后重試...", 'error')
185                     }
186                 }
187             });
188             //關閉裁剪框
189             closeTailor();
190         });
191         //關閉裁剪框
192         function closeTailor() {
193             $(".tailoring-container").toggle();
194             mediaStreamTrack && mediaStreamTrack.stop();
195         }
196 
197         //訪問用戶媒體設備的兼容方法
198         function getUserMedia(constraints, success, error) {
199             if (navigator.mediaDevices.getUserMedia) {
200                 //最新的標准API
201                 navigator.mediaDevices.getUserMedia(constraints).then(success).catch(error);
202             } else if (navigator.webkitGetUserMedia) {
203                 //webkit核心瀏覽器
204                 navigator.webkitGetUserMedia(constraints, success, error)
205             } else if (navigator.mozGetUserMedia) {
206                 //firfox瀏覽器
207                 navigator.mozGetUserMedia(constraints, success, error);
208             } else if (navigator.getUserMedia) {
209                 //舊版API
210                 navigator.getUserMedia(constraints, success, error);
211             }
212         }
213 
214         let video = document.getElementById('video');
215         let canvas = document.getElementById('canvas');
216         let context = canvas.getContext('2d');
217         var mediaStreamTrack
218         function success(stream) {
219             //兼容webkit核心瀏覽器
220             let CompatibleURL = window.URL || window.webkitURL;
221             //將視頻流設置為video元素的源
222             mediaStreamTrack = stream.getTracks()[0];
223             //video.src = CompatibleURL.createObjectURL(stream);
224             video.srcObject = stream;
225             video.play();
226         }
227 
228         function error(error) {
229             alert('訪問用戶媒體設備失敗,請嘗試更換瀏覽器')
230         }
231 
232 
233 
234         document.getElementById('capture').addEventListener('click', function () {
235             context.drawImage(video, 0, 0, 480, 320);
236             mediaStreamTrack && mediaStreamTrack.stop();
237             $('#tailoringImg').cropper('replace', canvas.toDataURL("image/png"), false);//默認false,適應高度,不失真
238             $("#video").hide();//隱藏拍照框
239             $("#showImg").show()//將拍照結果顯示
240         })
241 
242         //請求拍照
243         $("#takeAgain").bind("click", function () {
244             takePhoto();
245         });
246 
247         //開始拍照
248         function takeImg() {
249             $(".tailoring-container").toggle();
250             takePhoto();
251         }
252 
253         //請求攝像頭
254         function takePhoto() {
255             if (navigator.mediaDevices.getUserMedia || navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia) {
256                 //調用用戶媒體設備, 訪問攝像頭
257                 getUserMedia({ video: { width: 100, height: 100 } }, success, error);
258                 $("#showImg").hide();//隱藏拍照結果顯示框
259                 //$('#showImg').html('<img id="tailoringImg" hidden="hidden">')
260                 $("#video").show();//開啟拍照框
261             } else {
262                 alert('不支持訪問用戶媒體');
263             }
264         }
265     </script>
266 
267 </body>
268 
269 </html>

3.C# 后台接收二維碼圖片,進行壓縮解析的相關代碼

 1         [HttpPost]
 2         public ActionResult UploadPhoto(/*[System.Web.Http.FromBody] photoinfo photo*/)
 3         {
 4             try
 5             {
 6                 string base64Str = Request.Form["base64url"];
 7                 base64Str = base64Str.Substring(base64Str.IndexOf("base64,") + 7);
 8 
 9                 byte[] imgBytes = Convert.FromBase64String(base64Str);
10                 Stream stream = new MemoryStream(imgBytes);
11                 var bit = new Bitmap(stream);
12                 string photoBase = $"{AppDomain.CurrentDomain.BaseDirectory}\\photoimg";
13                 if (!Directory.Exists(photoBase))
14                 {
15                     Directory.CreateDirectory(photoBase);
16                 }
17                 string guid = Guid.NewGuid().ToString();
18                 string oldPath = $"{photoBase}\\{guid}.png";
19                 string newPath = $"{photoBase}\\{guid}_1.png";
20                 bit.Save(oldPath);
21 
22                 var compress = CompressImage(oldPath, newPath);
23                 var bindInfo = new QrCodeInfo();
24 
25                 if (!compress)
26                 {
27                     var result = new ZXing.BarcodeReader().Decode(bit);
28                     bit.Dispose();
29                     if (result != null && !string.IsNullOrEmpty(result.Text))
30                     {
31                         bindInfo = Newtonsoft.Json.JsonConvert.DeserializeObject<QrCodeInfo>(result.Text);
32                     }
33                 }
34                 else
35                 {
36 
37                     var f = System.IO.File.Open(newPath, System.IO.FileMode.Open);
38                     var newbit = new Bitmap(f);
39                     var result = new BarcodeReader().Decode(newbit);
40                     f.Dispose();
41                     newbit.Dispose();
42                     if (result != null && !string.IsNullOrEmpty(result.Text))
43                     {
44                         bindInfo = Newtonsoft.Json.JsonConvert.DeserializeObject<QrCodeInfo>(result.Text);
45                     }
46                 }
47                 // bit.Save($"{photoBase}\\{Guid.NewGuid()}.png");
48 
49                 if (bindInfo != null)
50                 {
51                     //傳參,調用方法,顯示查詢查詢到的數據  
52                     var accountList = _iSysPadBindMobileCore.GetAccountsByMachineId(bindInfo.MachineId, bindInfo.Model);
53                     if (accountList.Any())
54                     {
55                         return Json(new { code = 1, msg = Newtonsoft.Json.JsonConvert.SerializeObject(accountList) }, JsonRequestBehavior.AllowGet);
56                     }
57                 }
58             }
59             catch (Exception ex)
60             {
61                 return Json(new { code = -1, msg = ex.ToString().Substring(0, 20) }, JsonRequestBehavior.AllowGet);
62             }
63 
64             return Json(new { code = -1, msg = "" }, JsonRequestBehavior.AllowGet);
65         }

PS:另外在上傳圖片的過程中,可能會因為圖片文件過大導致報500錯誤,只需要在web.config的<system.web></system.web>節點中配置大小即可:   <httpRuntime maxRequestLength="102400" executionTimeout="200" enable="true" />

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM