現在的手機拍攝的照片大小基本都在5M~10M之間。對於大圖片的上傳,不僅慢,而且對使用者體驗有嚴重的影響。如果我們對圖片清晰度的要求不是很高,可以通過前端的壓縮可以達到兩個目的:1.節省流量。 2.提高使用者體驗。
1.把系統中的圖片呈現在瀏覽器中
html程式碼:
<input id="J_takepic" accept="image/*" type="file">
<img id="J_showpic" src="" alt="show-picture">
<canvas id="J_canvas"></canvas>
js程式碼:
let takePic = document.querySelecotr('#J_takepic')
let showPic = document.querySelecotr('#J_showpic')
複製程式碼
獲取圖片: 目前獲取input圖片的方法主要有兩種:
(1)FileReader (2)createObjectURL
if(tackPic && showPic) {
takePic.onchange = function(event) {
let files = event.target.files
let file = ''
if (files && files.lenght > 0) {
file = files[0]
try {
let URL = window.URL || window.webkitURL
let imgURL = URL.createObjectURL(file)
showPic.src = imgURL
showPic.onload = function () {
URL.revokeObjectURL(imgURL)
}
}
catch (e) {
try {
let fileReader = new FileReader()
fileReader.onload = function (event) {
showPic.src = event.target.result
}
fileReader.readAsDataURL(file)
}
catch (e) {
console.error('Neither createObjectURL or FileReader are supported')
}
}
}
}
}
複製程式碼
2.獲取圖片旋轉度
並不是所有的手機拍攝的圖片在img標籤中都可以正常展示,在測試不同手機的過程中你會驚訝的發現有些圖片竟然被旋轉了90度。作為一名程式猿,這種問題怎麼能忍。 正常情況下,手機拍攝的照片都會攜帶地址、旋轉角度、大小等資訊,通過一定的方法都可以獲取到。這裡我們通過EXIF來獲取圖片旋轉角度。
let Orientation = 1
EXIF.getData(file, function() {
Orientation = EXIF.getTag(this, 'Orientation');
})
Orientation的值分別為:1(無旋轉)6(旋轉90度)3(旋轉180度)8(旋轉-90度)
複製程式碼
3.旋轉並壓縮圖片
旋轉圖片的實現基於canvas的rotate()方法。旋轉的中心點預設在canvas的(0,0)點。
利用canvas.toDataURL()進行圖片壓縮,得到圖片的data uri的值。
function rotateAndCompress (image, Orientation) {
let imgWidthOrigin = image.width
let imgHeightOrigin = image.height
// 壓縮圖片
let ratio = imgWidthOrigin / imgHeightOrigin
// 假設壓縮後的圖片的寬度為500px
let canvasWidth = 500
let canvasHeight = Math.ceil(500 / ratio)
// 旋轉並壓縮
let canvas = document.getElementById('canvas')
let ctx = canvas.getContext('2d')
canvas.width = canvasWidth
canvas.height = canvasHeight
if (Orientation && Orientation !== 1) {
switch (Orientation) {
case 6:
canvas.width = canvasHeight
canvas.height = canvasWidth
ctx.rotate(90 * Math.PI / 180)
ctx.drawImage(image, 0, -canvasHeight, canvasWidth, canvasHeight)
break
case 3:
ctx.rotate(Math.PI)
ctx.drawImage(image, -canvasWidth, -canvasHeight, canvasWidth,canvasHeight)
break
case 8:
// 旋轉-90度相當於旋轉了270度
canvas.width = canvasHeight
canvas.height = canvasWidth
ctx.rotate(270 * Math.PI / 180)
ctx.drawImage(image, -canvasWidth, 0, canvasWidth, canvasHeight)
break
}
} esle {
ctx.drawImage(image, 0, 0, canvasWidth, canvasHeight)
}
}
複製程式碼
到了這裡,我們基本完成了對圖片的旋轉與壓縮。通常我們的寫法如下:
let Orientation = 1
EXIF.getData(file, function() {
Orientation = EXIF.getTag(this, 'Orientation');
})
rotateAndCompress(Orientation) // 立即呼叫旋轉和壓縮的方法
複製程式碼
在自己的手機上測試之後,發現圖片正常的進行了旋轉,檢視壓縮後的圖片,圖片從6.8M壓縮到了1.1M,壓縮效果顯著。心裡美滋滋...
換了臺手機再次測試,竟然發現圖片沒有旋轉的情況依舊沒有改變。what?明明不是已經獲取到圖片的旋轉角度了麼,為什麼有的手機可以正常旋轉,有的手機卻依舊存在問題呢?debug...
調式之後竟然發現Orientation竟然沒有獲取到,原來是回撥函式EXIF.getTag()還沒有返回結果就已經執行了rotateAndCompress()方法(高效能的手機執行速度確實快)。最終的解決方案如下:利用promise解決了非同步回撥的問題,等待獲取到Orientation之後再執行rotateAndCompress()
let promise = new Promise((resolve,reject) => {
EXIF.getData(file, function() {
Orientation = EXIF.getTag(this, 'Orientation');
resolve(Orientation)
})
})
promise.then((Orientation) => {
rotateAndCompress(Orientation)
})
複製程式碼
問題順利解決,繼續心中美滋滋...
4.把canvas畫布轉換成img影象,目前常用的方法有兩種 (1)toDataURL(2)toBolb
showPic.src = canvas.toDataURL(mimeType, qualityArgument)
showPic.src = canvas.toBolb(callback,mineType, qualityArgument)
複製程式碼
區別: (1)toDataURL:是把圖片轉換成base64格式資訊,純字元的圖片表示法。mimeType表示匯出的base64圖片型別預設是png,即'image/png',也可以為 'image/jpeg'或webp等格式。qualityArgument表示匯出圖片的質量,只有匯出圖片為jpg和webp時才有效果,預設是0.92. (2)toBlob:是把canvas轉換成Blob檔案(二進位制檔案),通常用於檔案上傳。 XMLHttpRequest 2.0的家臣們對這些進行了詳細的講解。
function dataURItoBlob (base64Data) {
//去掉url的頭,並轉換為byte
let bytes = window.atob(base64Data.split(',')[1])
// 處理異常,將ascii碼小於0的轉換為大於0
let ab = new ArrayBuffer(bytes.length)
// 生成檢視(直接針對記憶體):8位無符號整數,長度1個位元組
let ia = new Uint8Array(ab)
for (let i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i)
}
return new Blob([ab], {
type: 'image/jpg'
})
}
複製程式碼
5.使用FormaData將生成的blob檔案上傳
利用FormData對像,我們可以通過js來模擬一系列表單控制元件。FormData的最大優點是可以非同步上傳二進位制檔案。
let formData = new FormData()
formData.append('photo', blob, imageName)
$.ajax({
type: 'post',
url: "xxx/file/upload",
data: formData,
processData: false,
traditional: true,
contentType: false,
}).success(function (res) {
console.log(res);
}).error(function () {
console.log("upload fail");
})
複製程式碼
注: EXIF獲取圖片旋轉度程式碼
EXIF.getData = function(img, callback) {
if (((self.Image && img instanceof self.Image) || (self.HTMLImageElement && img instanceof self.HTMLImageElement)) && !img.complete)
return false
if (!imageHasData(img)) {
getImageData(img, callback)
} else {
if (callback) {
callback.call(img)
}
}
return true
}
EXIF.getTag = function (img, tag) {
if (!imageHasData(img)) return
return img.exifdata[tag]
}
function imageHasData(img) {
return !!(img.exifdata)
}
function getImageData(img, callback) {
function handleBinaryFile(binFile) {
var data = findEXIFinJPEG(binFile)
img.exifdata = data || {}
var iptcdata = iptcdata || {}
if (EXIF.isXmpEnabled) {
var xmpdata = findXMPinJPEG(bindFile)
img.xmpdata = xmpdata || {}
}
if (callback) {
callback.call(img)
}
}
}
複製程式碼