爲何要前端來壓縮圖片
最近在作一個移動端h5上傳圖片的功能,原本這個功能並不複雜,只須要將圖片文件經過axios傳到服務端便可,可是考慮到如今手機設配的拍照功能十分強大,隨便一張照片都能動輒五六兆,而服務端的要求是上傳圖片必須小於兩兆,並且直接傳這麼大圖片,帶寬它也受不了,因此前端進行壓縮圖片就成了一個必要的環節。javascript
考慮到文章和步驟的完整性,因此我會把每一個細節都寫出來,即便有些東西很基礎。前端
這一步你們應該最熟悉不過了吧,原生input標籤,經過設置type
屬性爲file來讓用戶能夠選擇文件,設置accept
限制選擇的文件類型,綁定onchange事件,來獲取確認選擇後的文件java
<input type="file" accept="image/*" />複製代碼
點擊控件,觸發焦點,打開文件資源管理器,選中文件並確認後,會觸發change事件,因此能夠在change事件的回調中獲取選中文件,它長這個樣ios
拿到圖片文件後,先將其轉成HTMLImageElement,也就是普通的img標籤,具體要使用FileReader構造函數。web
先new出來一個img和fileReader的實例,經過fileReader的readAsDataURL這個api,來讀取圖片文件,其返回值是一個編碼後的base64的字符串,而後將這個字符串賦值給img的src屬性上,這樣就完成了圖片文件到HTMLImageElement的轉化。json
// 先new一個img和fileReader的實例
const img = new Image()
const reader = new FileReader()// 讀取文件資源
reader.readAsDataURL(file)
reader.onload = function(e){
img.src = e.target.result
}複製代碼
轉化的HTMLImageElement
拿到轉化後的img元素後,先取出該元素的寬高度,這個寬高度就是實際圖片文件的寬高度。canvas
const { width: originWidth, height: originHeight } = img複製代碼
而後定義一個最大限度的寬高度,若是超過這個限制寬高度,則進行等比例的縮放axios
// 最大尺寸限制
const maxWidth = 1000,maxHeihgt = 1000
// 須要壓縮的目標尺寸
let targetWidth = originWidth, targetHeight = originHeight
// 等比例計算超過最大限制時縮放後的圖片尺寸
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > 1) {
// 寬圖片
targetWidth = maxWidth
targetHeight = Math.round(maxWidth * (originHeight / originWidth))
} else {
// 高圖片
targetHeight = maxHeight
targetWidth = Math.round(maxHeight * (originWidth / originHeight))
}
}複製代碼
計算好將要壓縮的尺寸後,建立canvas實例,設置canvas的寬高度爲壓縮計算後的尺寸,並將img繪製到上面api
// 建立畫布
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')
// 設置寬高度爲等同於要壓縮圖片的尺寸
canvas.width = targetWidth
canvas.height = targetHeight
context.clearRect(0, 0, targetWidth, targetHeight)
//將img繪製到畫布上
context.drawImage(img, 0, 0, targetWidth, targetHeight)複製代碼
canvas繪製完成後,就可使用toBlob來將圖像轉成blob文件了,這個api接受三個入參bash
canvas.toBlob(callback, type, encoderOptions);複製代碼
回調函數中能夠獲得轉化後的blob文件,type爲要轉成的圖片類型,默認png。
encoderOptions爲當設置的圖片格式爲image/jpeg
或者image/webp
時用來指定圖片展現質量。
因此若是咱們只是要壓縮jpg或者webp格式的圖片的話,不須要進行第3部的操做,直接使用這個api,而後填入想要的質量參數就能夠了。但實際上,咱們仍是要考慮多種的圖片格式,所以頗有必要使用第三部的過程。
轉成的blob長這個樣子
由於整個過程當中都存在着異步回調操做,因此我使用了async,實現異步代碼的同步執行
// 壓縮前將file轉換成img對象
function readImg(file) {
return new Promise((resolve, reject) => {
const img = new Image()
const reader = new FileReader()
reader.onload = function(e) {
img.src = e.target.result
}
reader.onerror = function(e) {
reject(e)
}
reader.readAsDataURL(file)
img.onload = function() {
resolve(img)
}
img.onerror = function(e) {
reject(e)
}
})
}複製代碼
/**
* 壓縮圖片
*@param img 被壓縮的img對象
* @param type 壓縮後轉換的文件類型
* @param mx 觸發壓縮的圖片最大寬度限制
* @param mh 觸發壓縮的圖片最大高度限制
*/
function compressImg(img, type, mx, mh) {
return new Promise((resolve, reject) => {
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')
const { width: originWidth, height: originHeight } = img
// 最大尺寸限制
const maxWidth = mx
const maxHeight = mh
// 目標尺寸
let targetWidth = originWidth
let targetHeight = originHeight
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > 1) {
// 寬圖片
targetWidth = maxWidth
targetHeight = Math.round(maxWidth * (originHeight / originWidth))
} else {
// 高圖片
targetHeight = maxHeight
targetWidth = Math.round(maxHeight * (originWidth / originHeight))
}
}
canvas.width = targetWidth
canvas.height = targetHeight
context.clearRect(0, 0, targetWidth, targetHeight)
// 圖片繪製
context.drawImage(img, 0, 0, targetWidth, targetHeight)
canvas.toBlob(function(blob) {
resolve(blob)
}, type || 'image/png') })
}複製代碼
大體執行過程,具體可根據需求,自行改動
async function upload(file){
const img = await readImg(file)
const blob = await compressImg(img, file.type, 1000, 1000)
const formData = new FormData()
formData.append('file', blob, 'xxx.jpg')
axios.post('http://xxx.com/api',formData)
}
upload(file).catch(e => console.log(e))複製代碼
感謝您的閱讀,但願能和您共同進步,覺的有幫助的話,點個贊哦😁😁