JS獲取剪貼板圖片之后的格式選擇與壓縮問題


前言

某年某月的某一天,突然發現博客服務器上上傳的圖片都比較大,一些很小的截圖都有幾百kb,本來服務器帶寬就慢,不優化一下說不過去。

問題細述

特別說明:本文代碼因為只是用於我自己后台寫markdown上傳圖片,運行環境只考慮PC,所以沒有考慮任何兼容性,推薦Chrome下使用。

以下面一張圖片為例:

原始圖片為85kb,jpg格式的,上傳之后就變成png格式了,而且變成了560kb!實在是說不過去!

我的代碼大致如下:

// $('.editor')為markdown編輯區
$('.editor').bind('paste', function(event)
{
	var e = event.originalEvent;
	var items = e.clipboardData.items;
	for(var i = 0; i < items.length; ++i)
	{
		// 如果剪貼板中的內容類型是圖片文件
		if (items[i].kind == 'file' && items[i].type.indexOf('image/') >= 0)
		{
			e.preventDefault();
			var blob = items[i].getAsFile();
			ajaxUpload(blob); // 拿到blob之后直接上傳,這里省略上傳代碼
		};
	};
});

我原本以為圖片原始是多大,上傳之后就應該是多大的,因為是“復制粘貼”嘛,可見實際情況並非如此,問題出在哪個環節呢?

我將相同圖片采用復制粘貼的方式粘貼到word里面去,然后另存為html格式,發現保存下來的圖片更大了,有將近900kb。

雖然如此,直到現在依然不能確定,圖片的轉換是在當我們打開圖片按下Ctrl+C放到剪貼板的時候就已經執行了,還是在js中items[i].getAsFile()中轉換了,有知道的朋友歡迎指正。

問題解決

既然圖片變大了,那就壓縮嘛,壓縮一般習慣在前端進行,這樣可以降低上傳帶寬。

處理過程:先從剪貼板獲取圖片,拿到的是blob對象,然后轉換成base64格式的dataURL,再然后以Image形式畫到canvas上,再把canvas轉換成dataURL,這個轉換的過程可以設置質量,最后再將dataURL轉換成blob,然后上傳。

直接上代碼:

/**
 * 改變blob圖片的質量,為考慮兼容性
 * @param blob 圖片對象
 * @param callback 轉換成功回調,接收一個新的blob對象作為參數
 * @param format 目標格式,mime格式
 * @param quality 介於0-1之間的數字,用於控制輸出圖片質量,僅當格式為jpg和webp時才支持質量,png時quality參數無效
 */
function changeBlobImageQuality(blob, callback, format, quality)
{
	format = format || 'image/jpeg';
	quality = quality || 0.9; // 經測試0.9最合適
	var fr = new FileReader();
	fr.onload = function(e)
	{
		var dataURL = e.target.result;
		var img = new Image();
		img.onload = function()
		{
			var canvas = document.createElement('canvas');
			var ctx = canvas.getContext('2d');
			canvas.width = img.width;
			canvas.height = img.height;
			ctx.drawImage(img, 0, 0);
			var newDataURL = canvas.toDataURL(format, quality);
			if(callback) callback(dataURLtoBlob(newDataURL));
			canvas = null;
		};
		img.src = dataURL;
	};
	fr.readAsDataURL(blob); // blob 轉 dataURL
	function dataURLtoBlob(dataurl)
	{
		var arr = dataurl.split(','),
			mime = arr[0].match(/:(.*?);/)[1],
			bstr = atob(arr[1]),
			len = bstr.length,
			u8arr = new Uint8Array(len);
		while (len--) u8arr[len] = bstr.charCodeAt(len);
		return new Blob([u8arr], {type: mime});
	}
}

修改之后的上傳代碼就是這樣了:

if (items[i].kind == 'file' && items[i].type.indexOf('image/') >= 0)
{
	e.preventDefault();
	var blob = items[i].getAsFile();
	changeBlobImageQuality(blob, function(newBlob)
	{
		ajaxUpload(newBlob); // 拿到blob之后直接上傳,這里省略上傳代碼
	});
};

關於dataURL轉blob,有一個考慮的兼容性的代碼片段如下:

function convertDataURLToBlob(dataURL)
{
	var arr = dataURL.split(',');
	var code = window.atob(arr[1]);
	var aBuffer = new window.ArrayBuffer(code.length);
	var uBuffer = new window.Uint8Array(aBuffer);
	for(var i = 0; i < code.length; i++)
	{
		uBuffer[i] = code.charCodeAt(i);
	}
	var Builder = window.WebKitBlobBuilder || window.MozBlobBuilder;
	if(Builder)
	{
		var builder = new Builder;
		builder.append(buffer);
		return builder.getBlob(format);
	}
	else
	{
		return new window.Blob([ buffer ], {type: arr[0].match(/:(.*?);/)[1]});
	}
}

問題還沒完全解決

有一個問題,對於GIF格式的圖片,復制粘貼之后只會復制第一幀(不知高手是否有解決方案)。

還有一個問題,雖然我要解決的問題是圖片上傳之后變大,但是有時候本地本來就是一張壓縮的比較厲害的jpg格式圖片,上傳之后雖然通過調整質量可以控制大小,但是有時候經過前端這么一折騰,體積雖然變化不大,但是圖片質量下降的很厲害,特別是對於截圖又添加文字的圖片,比如下面這個:

這是png格式效果,上傳之后28kb:

這是jpg效果,上傳之后37kb,反而比png格式還要大,而且質量大打折扣:

發現貌似是在上傳一些底色為純色的圖片時,png比jpg更好,在上傳傳統的類似風景圖片時,jpg格式更好,不知道說的有沒有錯。

還有很多時候我是希望圖片沒有任何轉換,直接原樣上傳,所以簡單增加了個拖拽上傳的功能,直接把事件綁定在body上面:

function initDrag()
{
	var obj = $('body')[0];
	obj.ondragenter = function(ev)
	{
		ev.dataTransfer.dropEffect = 'copy';
	};
	obj.ondragover = function(ev)
	{
		ev.preventDefault(); //防止默認事件拖入圖片 放開的時候打開圖片了
		ev.dataTransfer.dropEffect = 'copy';
	};
	obj.ondrop = function(ev)
	{
		ev.preventDefault();
		var files = ev.dataTransfer.files;
		for(var i=0; i<files.length; i++)
		{
			if(files[i].type.indexOf('image') >= 0)
			{
				ajaxUpload(files[i]);
			}
		}
	};
}

這樣我就可以根據實際需要選擇不同方式上傳圖片了:

  • 普通的白底黑字截圖的話就上傳png格式;
  • 普通復雜背景圖片就上傳jpg格式;
  • 一些gif格式還有一些本來就比較小的圖片,就采用拖拽上傳的方式;


免責聲明!

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



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