base64圖片儲存超過2M的解決方案
很多情況下使用者上傳的圖片都需要經過裁剪,比如頭像啊什麼的。但以前實現這類需求都很複雜,往往需要先把圖片上傳到伺服器,然後返回給使用者,讓使用者確定裁剪座標,傳送給伺服器,伺服器裁剪完再返回給使用者,來回需要 5 步。步驟繁瑣不說,當很多使用者上傳圖片的時候也很影響伺服器效能。
HTML5 的出現讓我們可以更方便的實現這一需求。雖然這裡所說的技術都貌似有點過時了(前端界的“過時”,你懂的),但還是有些許參考價值。在這裡我只說一下要點,具體實現同學們慢慢研究。
下面奉上我自己寫的一個demo,在輸入框中選好自己伺服器 url, 生成好圖片後點擊 Submit 上傳,然後自己去伺服器裡看看效果吧~~
瀏覽器要求支援以下 Feature:
程式碼直接從現有專案移植過來,沒有經過“太多的”測試,寫的很亂,也沒註釋,大家就慢慢看吧。。。重點就在 js 指令碼的 28 行,clipImage
函式中,同學們可以直接跳過去看。
第一步:獲取檔案
HTML5 支援從 input[type=file]
元素中直接獲取檔案資訊,也可以讀取檔案內容。我們用下面程式碼就可以實現:
$('input[type=file]').change(function(){
var file=this.files[0];
// continue ...
});
第二部:讀取檔案,並生成 Image
元素
這一步就需要用到 FileReader
了,這個類是專門用來讀取本地檔案的。純文字或者二進位制都可以讀取,但是本地檔案必須是經過使用者允許才能讀取,也就是說使用者要在input[type=file]
通過 FileReader
我們可以將圖片檔案轉化成 DataURL
,就是以 data:image/png;base64,
開頭的一種URL,然後可以直接放在 image.src
裡,這樣本地圖片就顯示出來了。
$('input[type=file]').change(function(){
var file=this.files[0];
var reader=new FileReader();
reader.onload=function(){
// 通過 reader.result 來訪問生成的 DataURL
var url=reader.result;
setImageURL(url);
};
reader.readAsDataURL(file);
});
var image=new Image();
function setImageURL(url){
image.src=url;
}
Image
就是在 html
裡的 <img>
標籤,所以可以直接插入到文件流裡。
第三步:獲取裁剪座標
這一步沒啥好說的,實現的方法也很多,需要獲得下面四個裁剪框的座標:
- Y座標
- X座標
- 高度
- 寬度
如下圖所示:
第四部:裁剪圖片
這是時候我們就需要用到 canvas
了,canvas
和圖片一樣,所以新建 canvas
時就要確定其高寬。這裡我們還運用到 image.naturalHeight
和 image.naturalWidth
這兩個屬性來獲取圖片原始尺寸。
將圖片放置入 canvas
時需要呼叫 drawImage
,這個介面引數比較多,在 MDN 上有詳細的說明。
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
因為我們用 canvas
只是用於裁剪圖片的,所以需要新建一個 canvas
讓它的尺寸和裁剪之後圖片的尺寸相等,此時 canvas
就相當與我們的裁剪框。運用這個函式還可以將大圖縮放成小圖,同學們自己研究吧。
// 以下四個引數由第三步獲得
var x,
y,
width,
height;
var canvas=$('<canvas width="'+width+'" height="'+height+'"></canvas>')[0],
ctx=canvas.getContext('2d');
ctx.drawImage(image,x,y,width,height,0,0,width,height);
$(document.body).append(canvas);
將 canvas
加入文件流之後,就可以看到裁剪後的效果了。不過我們還需要將圖片上傳至伺服器裡。
第五步:讀取裁剪後的圖片並上傳
這時我們要獲取 canvas
中圖片的資訊,用 toDataURL
就可以轉換成上面用到的 DataURL
。 然後取出其中 base64 資訊,再用 window.atob
轉換成由二進位制字串。但 window.atob
轉換後的結果仍然是字串,直接給 Blob
還是會出錯。所以又要用 Uint8Array
轉換一下。總之這裡挺麻煩的。。
var data=canvas.toDataURL();
// dataURL 的格式為 “data:image/png;base64,****”,逗號之前都是一些說明性的文字,我們只需要逗號之後的就行了
data=data.split(',')[1];
data=window.atob(data);
var ia = new Uint8Array(data.length);
for (var i = 0; i < data.length; i++) {
ia[i] = data.charCodeAt(i);
};
// canvas.toDataURL 返回的預設格式就是 image/png
var blob=new Blob([ia], {type:"image/png"});
這時候裁剪後的檔案就儲存在 blob
裡了,我們可以把它當作是普通檔案一樣,加入到 FormData
裡,並上傳至伺服器了。
FormData
顧名思義,就是用來建立表單資料的,用 append
以鍵值的形式將資料加入進去即可。但他最大的特點就是可以手工新增檔案或者 Blob
型別的資料,Blob
資料也會被當作檔案來處理。原生 js 可以直接傳遞給 xhr.send(fd)
, jquery 可以放入 data
裡請求。
var fd=new FormData();
fd.append('file',blob);
$.ajax({
url:"your.server.com",
type:"POST",
data:fd,
success:function(){}
});
然後你伺服器裡應該就可以收到這個檔案了~
https://segmentfault.com/a/1190000000754560