H5圖片壓縮與上傳實例
接到需求,問前端是否可以壓縮圖片?因為有的圖片太大,傳到服務(wù)器上再壓縮太慢了。意識里沒有這么玩過,早上老大丟來一個知乎鏈接,一看,原來前輩們已經(jīng)用canvas實現(xiàn)了(為自己的見識羞愧3秒鐘,再馬上開干)!。
canvas壓縮
使用了github上的一個現(xiàn)成庫:https://github.com/stomita/ios-imagefile-megapixel,不得不膜拜下stomita這位大神。大體的思路是將圖片抽樣顯示在canvas上,然后用通過canvas.toDataURL方法得到base64字符串來實現(xiàn)壓縮。比如在input元素觸發(fā)change事件之后,讀取里面的文件進(jìn)行操作:
var fileInput = document.getElementById('fileInput');
fileInput.onchange = function() {
var file = fileInput.files[0];
// 創(chuàng)建一個壓縮對象,該構(gòu)造函數(shù)接收file或者blob。
var mpImg = new MegaPixImage(file);
// render方法的maxWith,maxHeight,以及quality都決定了壓縮圖片的質(zhì)量
var resImg = document.getElementById('resultImage');
mpImg.render(resImg, { maxWidth: 300, maxHeight: 300, quality: 0.5 });
};
壓縮完成會得到
類似這樣的圖片:

data:image/jpeg 這樣的格式已經(jīng)應(yīng)用的很多了,很多樣式里面的背景圖片直接就是這樣。
需要說明的是有兩點,這里的resImg是一個預(yù)覽圖片,是已經(jīng)存在于文檔中的,如果你不需要預(yù)覽,而只是創(chuàng)建一個img用來壓縮(document.createElement("img")),這會少一個tagName屬性。你可以修改源碼或者自己加上這個屬性。源碼中會根據(jù)tagName進(jìn)行判斷,不存在的話會報錯:
MegaPixImage.prototype.render = function (target, options, callback) {
//....
target.tagName = target.tagName || "IMG"; //加上這一句
var tagName = target.tagName.toLowerCase();
if (tagName === 'img') {
target.src = renderImageToDataURL(this.srcImage, opt, doSquash);
} else if (tagName === 'canvas') {
renderImageToCanvas(this.srcImage, target, opt, doSquash);
}
if (typeof this.onrender === 'function') {
this.onrender(target);
}
if (callback) {
callback();
}
if (this.blob) {
this.blob = null;
URL.revokeObjectURL(this.srcImage.src);
}
};
另外這是一個耗時的操作,如果是多張圖片進(jìn)行壓縮,不能直接調(diào)用,需要稍微變換一下,不然會導(dǎo)致前面的圖片沒有壓縮完成就進(jìn)入到了后面的圖片。
fileSelected: function () {
var files = $("#fileImage")[0].files;
var count = files.length;
console.log("共有" + count + "個文件");
for (var i = 0; i < count; i++) {var item = files[i];
console.log("原圖片大小", item.size);
if (item.size > 1024 * 1024 * 2) {
console.log("圖片大于2M,開始進(jìn)行壓縮...");
(function(img) {
var mpImg = new MegaPixImage(img);
var resImg = document.createElement("img");
resImg.file = img;
mpImg.render(resImg, { maxWidth: 500, maxHeight: 500, quality: 1 }, function() {
//do some thing
});
})(item);
}
core.previewImage(item);
}
},
上傳處理
1.直接post base64字符串
uploadBase64str: function (base64Str) {
var formdata = new FormData();
formdata.append("base64str", base64Str);
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", function (e) {
var percentComplete = Math.round(e.loaded * 100 / e.total);
para.onProgress(percentComplete.toString() + '%');
});
xhr.addEventListener("load", function (e) {
para.uploadComplete(xhr.responseText);
});
xhr.addEventListener("error", function (e) {
para.uploadError(e);
});
xhr.open("post", para.base64strUrl, true);
xhr.send(formdata);
},
比如這里base64strUrl是/home/MUploadImgBase64Str,MVC控制器方法如下:
[HttpPost]
public ActionResult MUploadImgBase64Str(string base64str)
{
try
{
var imgData = base64str.Split(',')[1];
//過濾特殊字符即可
string dummyData = imgData.Trim().Replace("%", "").Replace(",", "").Replace(" ", "+");
if (dummyData.Length % 4 > 0)
{
dummyData = dummyData.PadRight(dummyData.Length + 4 - dummyData.Length % 4, '=');
}
byte[] byteArray = Convert.FromBase64String(dummyData);
using (System.IO.MemoryStream ms = new System.IO.MemoryStream(byteArray))
{
var img = System.Drawing.Image.FromStream(ms);
var path = "~/Content/UploadFiles/mobile/";
var uploadpath = Server.MapPath(path);
if (!Directory.Exists(uploadpath))
{
Directory.CreateDirectory(uploadpath);
}
var saveName = uploadpath + “stoneniqiu” + ".jpg";
img.Save(saveName);
return Json(saveName);
}
}
catch (Exception e)
{
return Json(e.Message);
}
}
幾M的圖片能壓縮到幾十k或者幾百k,當(dāng)然,如果寬度、高度和質(zhì)量設(shè)置的太小,圖片就會很失真了。這個字符串怎么獲取呢?有兩個方法,一個是直接讀取src:
var base641 = resImg.src;
一個是用canvas轉(zhuǎn)換:
function getBase64Image(img) {
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, img.width, img.height);
var dataURL = canvas.toDataURL("image/jpeg");
return dataURL;
// return dataURL.replace("data:image/png;base64,", "");
}
var base64 = getBase64Image(resImg);
同一張圖片,這兩者獲取到的字符串大小不一樣,但圖片質(zhì)量我是分辨不出什么差別。

比如一個2M的圖片,通過getBase64Image方法讀到的字符串大小才64k,而src直接讀取到的卻是270k,各自生成的圖片更小。一下分別是原圖(2.2M),base64(48k),src(202k)對應(yīng)的圖片。



getBase64Image通過canvas的toDataURL 獲取到更小的base64字符串。
2.也可以在前端轉(zhuǎn)換blob對象,再post到后端
function dataURItoBlob(dataUrl) {
var byteString = atob(dataUrl.split(',')[1]);
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], { type: 'image/jpeg' });
}
3.不壓縮的就直接裝到formdata中,send到后臺。
uploadFile: function (file) {
console.log("開始上傳");
var formdata = new FormData();
formdata.append(para.filebase, file);//這個名字要和mvc后臺配合
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", function (e) {
var percentComplete = Math.round(e.loaded * 100 / e.total);
para.onProgress(percentComplete.toString() + '%');
});
xhr.addEventListener("load", function (e) {
para.uploadComplete(xhr.responseText);
});
xhr.addEventListener("error", function (e) {
para.uploadError(e);
});
xhr.open("post", para.url, true);
xhr.send(formdata);
},
全部的代碼:(包含壓縮和上傳以及demo):
github:https://github.com/stoneniqiu/h5upload/
小結(jié):基本上就是這樣了,前端能夠壓縮圖片的話,確實省了流量和時間。 插件是在上一篇的基礎(chǔ)上進(jìn)行改進(jìn)的。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
AngularJS實現(xiàn)textarea記錄只能輸入規(guī)定數(shù)量的字符并顯示
AngularJS 是一個 MV* 框架,最適于開發(fā)客戶端的單頁面應(yīng)用。它不是個功能庫,而是用來開發(fā)動態(tài)網(wǎng)頁的框架。接下來通過本文給大家介紹AngularJS實現(xiàn)textarea記錄只能輸入規(guī)定數(shù)量的字符并顯示的相關(guān)內(nèi)容,需要的朋友參考下吧2016-04-04
javascript之textarea打字機(jī)效果提示代碼推薦
非常不錯的提示輸入內(nèi)容,動態(tài)的提示,給人親切感2008-09-09
js實現(xiàn)的鼠標(biāo)滾輪滾動切換頁面效果(類似360默認(rèn)頁面滾動切換效果)
這篇文章主要介紹了js實現(xiàn)的鼠標(biāo)滾輪滾動切換頁面效果,類似360默認(rèn)頁面滾動切換效果.涉及JavaScript響應(yīng)鼠標(biāo)事件動態(tài)變換頁面元素的相關(guān)技巧,需要的朋友可以參考下2016-01-01

