这几天,对图片进行了大量的研究。
获取图片的真实大小 1 2 3 4 5 const img = new Image ();img.src = 'https://www.baidu.com/img/bd_logo1.png' ; img.onload = () => { console .log (img.width , img.height ); };
但是,这个方法可能获取到得是图片的显示大小,而不是真实大小。真实大小可以用 img.naturalWidth
和 img.naturalHeight
来获取。
1 2 3 4 5 const img = new Image ();img.src = 'https://www.baidu.com/img/bd_logo1.png' ; img.onload = () => { console .log (img.naturalWidth , img.naturalHeight ); };
提取 Exif 数据 可以用 exif-js 库 - https://github.com/exif-js/exif-js
1 2 3 4 5 6 7 8 9 const EXIF = require ('exif-js' );const img = new Image ();img.src = 'https://www.baidu.com/img/bd_logo1.png' ; img.onload = () => { EXIF .getData (img, function ( ) { const allMetaData = EXIF .getAllTags (this ); console .log (allMetaData); }); };
设置图片的 Exif 信息 可以用 Piexifjs 库 - https://github.com/hMatoba/piexifjs
这个文章做了大量的研究和介绍 - https://auth0.com/blog/read-edit-exif-metadata-in-photos-with-javascript/
1 npm install piexifjs@1.0.4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 import piexifjs from 'piexifjs' ;document .getElementById ('saveButton' ).onclick = function ( ) { var zerothIfd = {}; var exifIfd = {}; var gpsIfd = {}; zerothIfd[piexif.ImageIFD .Make ] = 'Maker Name' ; zerothIfd[piexif.ImageIFD .XResolution ] = [777 , 1 ]; zerothIfd[piexif.ImageIFD .YResolution ] = [777 , 1 ]; zerothIfd[piexif.ImageIFD .Software ] = 'Piexifjs' ; exifIfd[piexif.ExifIFD .DateTimeOriginal ] = '2010:10:10 10:10:10' ; exifIfd[piexif.ExifIFD .LensMake ] = 'Lens Maker' ; exifIfd[piexif.ExifIFD .Sharpness ] = 777 ; exifIfd[piexif.ExifIFD .LensSpecification ] = [ [1 , 1 ], [1 , 1 ], [1 , 1 ], [1 , 1 ], ]; gpsIfd[piexif.GPSIFD .GPSVersionID ] = [7 , 7 , 7 , 7 ]; gpsIfd[piexif.GPSIFD .GPSDateStamp ] = '1999:99:99 99:99:99' ; var lat = 59.43553989213321 ; var lng = 24.73842144012451 ; gpsIfd[piexif.GPSIFD .GPSLatitudeRef ] = lat < 0 ? 'S' : 'N' ; gpsIfd[piexif.GPSIFD .GPSLatitude ] = piexif.GPSHelper .degToDmsRational (lat); gpsIfd[piexif.GPSIFD .GPSLongitudeRef ] = lng < 0 ? 'W' : 'E' ; gpsIfd[piexif.GPSIFD .GPSLongitude ] = piexif.GPSHelper .degToDmsRational (lng); var exifObj = { '0th' : zerothIfd, Exif : exifIfd, GPS : gpsIfd }; var exifBytes = piexif.dump (exifObj); var jpegData = document .getElementById ('canvas' ).toDataURL ('image/jpeg' , 1.0 ); var exifModified = piexif.insert (exifBytes, jpegData); var image = new Image (); image.src = exifModified; image.width = 200 ; if (saveJpeg) { var jpegBinary = atob (exifModified.split (',' )[1 ]); var data = []; for (var p = 0 ; p < jpegBinary.length ; p++) { data[p] = jpegBinary.charCodeAt (p); } var ua = new Uint8Array (data); var blob = new Blob ([ua], { type : 'image/jpeg' }); image.onclick = saveJpeg (blob); } var el = $('<div></div>' ).append (image); $('#resized' ).prepend (el); };
裁剪图片(浏览器) 可以用 cropper.js 库 -
裁剪图片(NodeJS) - sharp
1 2 3 4 5 6 7 import sharp from 'sharp' ;sharp ('input.jpg' ) .resize (200 , 300 ) .toFile ('output.jpg' , (err, info ) => { });
微信小程序提取图片 base64 参考文章:
1 2 3 4 5 6 7 8 9 10 11 12 13 wx.canvasToTempFilePath ({ canvasId : 'canvas' , success : function (res ) { console .log (res.tempFilePath ); wx.getFileSystemManager ().readFile ({ filePath : res.tempFilePath , encoding : 'base64' , success : (res ) => { console .log (res.data ); }, }); }, });
上面这个方法有个问题,就是它给出的 base64 为 png 格式,而不是 jpg 格式
但是后来我发现一些更好的方法,可以直接获取 jpg 格式的 base64
1 2 3 4 5 6 7 8 9 10 wx.canvasGetImageData ({ canvasId : 'canvas' , x : 0 , y : 0 , width : 100 , height : 100 , success : function (res ) { console .log (res.data ); }, });
微信小程序- 本地临时文件转 base64 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 function wxfileTobase64 (url, ishead = false ) { return new Promise ((reslove, reject ) => { wx.getFileSystemManager ().readFile ({ filePath : url, encoding : 'base64' , success : (res ) => { reslove ((ishead ? 'data:image/jpeg;base64,' : '' ) + res.data ); }, fail : (err ) => { reject (err); }, }); }); } wx.chooseImage ({ count : 4 , sizeType : ['compressed' ], sourceType : ['album' , 'camera' ], success (res ) { wxfileTobase64 (res.tempFilePaths [0 ]).then ((base64 ) => { console .log (base64); }); }, });
微信小程序 - base64 转本地临时文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function base64ToWxfile (base64, fileName ) { const path = wx.env .USER_DATA_PATH + '/' + fileName; return new Promise ((reslove, reject ) => { wx.getFileSystemManager ().writeFile ({ filePath : path, data : base64, encoding : 'base64' , success : (res ) => { reslove (path); }, fail : (err ) => { reject (err); }, }); }); }
微信小程序 - URL 转 base64 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function urlTobase64 (url, ishead = false ) { return new Promise ((reslove, reject ) => { wx.request ({ url : url, responseType : 'arraybuffer' , success : (res ) => { let base64 = wx.arrayBufferToBase64 (res.data ); base64 = (ishead ? 'data:image/jpeg;base64,' : '' ) + base64; reslove (base64); }, }); }); }
微信小程序 - canvas 转本地临时文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 wx.canvasToTempFilePath ({ x : 0 , y : 0 , width : 350 , height : 450 , canvasId : 'myCanvas' , success : function (res ) { let img = res.tempFilePath ; }, fail (err ) { console .log (err); }, });
bas64 转 blob 上传 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 getBlobBydataURI (dataURI, type ) { var binary = atob (dataURI.split (',' )[1 ]); var array = []; for (var i = 0 ; i < binary.length ; i++) { array.push (binary.charCodeAt (i)); } return new Blob ([new Uint8Array (array)], { type : type }); }, base64Upload (base64, url ) { var $Blob = this .getBlobBydataURI (base64, 'image/png' ); var formData = new FormData (); formData.append ("file" , $Blob, "avatar.png" ); var request = new XMLHttpRequest (); request.open ("POST" , url); request.onreadystatechange = function ( ) { if (request.readyState == 4 ) { if (request.status == 200 ) { console .log ("上传成功" , request); } else { console .log ("上传失败,检查上传地址是否正确" , request); } } } request.send (formData); }
JS 判断 Base64 图片格式 1 2 3 4 5 6 function getBase64ImageType (base64 ) { var base64Data = base64.split (',' )[1 ]; var dataBuffer = Buffer .from (base64Data, 'base64' ); var type = fileType (dataBuffer); return type ? type.ext : null ; }
但其实上面的方法不对,真正的 png 和 jpg 还是有差异的,这篇文章也说明了原因
我在这篇文章中找到一个比较好的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 function base64FileHeaderMapper (fileBase64 ) { let fileHeader = new Map (); fileHeader.set ('/9j' , 'JPG' ); fileHeader.set ('iVB' , 'PNG' ); fileHeader.set ('Qk0' , 'BMP' ); fileHeader.set ('SUk' , 'TIFF' ); fileHeader.set ('JVB' , 'PDF' ); fileHeader.set ('UEs' , 'OFD' ); let res = '' ; fileHeader.forEach ((v, k ) => { if (k == fileBase64.substr (0 , 3 )) { res = v; } }); if (res == '' ) { res = 'unknown file' ; } return res; }
JS 获取图片 EXIF 旋转角度 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function getOrientation (file, callback ) { var reader = new FileReader (); reader.onload = function (e ) { var view = new DataView (e.target .result ); if (view.getUint16 (0 , false ) != 0xffd8 ) return callback (-2 ); var length = view.byteLength , offset = 2 ; while (offset < length) { var marker = view.getUint16 (offset, false ); offset += 2 ; if (marker == 0xffe1 ) { if (view.getUint32 ((offset += 2 ), false ) != 0x45786966 ) { return callback (-1 ); } var little = view.getUint16 ((offset += 6 ), false ) == 0x4949 ; offset += view.getUint32 (offset + 4 , little); var tags = view.getUint16 (offset, little); offset += 2 ; for (var i = 0 ; i < tags; i++) if (view.getUint16 (offset + i * 12 , little) == 0x0112 ) return callback (view.getUint16 (offset + i * 12 + 8 , little)); } else if ((marker & 0xff00 ) != 0xff00 ) break ; else offset += view.getUint16 (offset, false ); } return callback (-1 ); }; reader.readAsArrayBuffer (file); }
JS 旋转图片 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 function rotateImg (img, direction, canvas ) { const min_step = 0 ; const max_step = 3 ; if (img == null ) return ; var height = img.height ; var width = img.width ; var step = 2 ; if (step == null ) { step = min_step; } if (direction == 'right' ) { step++; step > max_step && (step = min_step); } else { step--; step < min_step && (step = max_step); } var degree = (step * 90 * Math .PI ) / 180 ; var ctx = canvas.getContext ('2d' ); switch (step) { case 0 : canvas.width = width; canvas.height = height; ctx.drawImage (img, 0 , 0 ); break ; case 1 : canvas.width = height; canvas.height = width; ctx.rotate (degree); ctx.drawImage (img, 0 , -height); break ; case 2 : canvas.width = width; canvas.height = height; ctx.rotate (degree); ctx.drawImage (img, -width, -height); break ; case 3 : canvas.width = height; canvas.height = width; ctx.rotate (degree); ctx.drawImage (img, -width, 0 ); break ; } }
base64 to blob url 1 2 3 4 5 6 7 8 9 10 11 function dataURLtoBlob (dataurl ) { var arr = dataurl.split (',' ), mime = arr[0 ].match (/:(.*?);/ )[1 ], bstr = atob (arr[1 ]), n = bstr.length , u8arr = new Uint8Array (n); while (n--) { u8arr[n] = bstr.charCodeAt (n); } return new Blob ([u8arr], { type : mime }); }
另外一种
1 2 3 4 5 6 7 8 9 const dataToBlob = async (imageData ) => { return await (await fetch (imageData)).blob (); }; const blob = await dataToBlob (destination.value );const url = URL .createObjectURL (blob);
base64 编码和解码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function base64Encode (str ) { return btoa ( encodeURIComponent (str).replace (/%([0-9A-F]{2})/g , function (match, p1 ) { return String .fromCharCode ('0x' + p1); }) ); } function base64Decode (str ) { return decodeURIComponent ( atob (str) .split ('' ) .map (function (c ) { return '%' + ('00' + c.charCodeAt (0 ).toString (16 )).slice (-2 ); }) .join ('' ) ); }
sharp 图片处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const sharp = require ('sharp' );const image = sharp (filename);const bbox = predictionResultObj.bboxes [i];const xMin = bbox[0 ];const xMax = bbox[1 ];const yMin = bbox[2 ];const yMax = bbox[3 ];const metadata = await image.metadata ();const imageWidth = metadata.width ;const imageHeight = metadata.height ;const left = Math .round (xMin * imageWidth);const top = Math .round (yMin * imageHeight);const width = Math .round ((xMax - xMin) * imageWidth);const height = Math .round ((yMax - yMin) * imageHeight);await image .extract ({ left, top, width, height }) .toFile (`output/${label} ${ids} .jpg` );
后记