1. 程式人生 > >base64圖片儲存超過2M的解決方案

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