1. 程式人生 > >HTML5 本地裁剪圖片並上傳至伺服器(老梗)

HTML5 本地裁剪圖片並上傳至伺服器(老梗)

很多情況下使用者上傳的圖片都需要經過裁剪,比如頭像啊什麼的。但以前實現這類需求都很複雜,往往需要先把圖片上傳到伺服器,然後返回給使用者,讓使用者確定裁剪座標,傳送給伺服器,伺服器裁剪完再返回給使用者,來回需要 5 步。步驟繁瑣不說,當很多使用者上傳圖片的時候也很影響伺服器效能。

HTML5 的出現讓我們可以更方便的實現這一需求。雖然這裡所說的技術都貌似有點過時了(前端界的“過時”,你懂的),但還是有些許參考價值。在這裡我只說一下要點,具體實現同學們慢慢研究。

下面奉上我自己寫的一個demo,在輸入框中選好自己伺服器 url, 生成好圖片後點擊 Submit 上傳,然後自己去伺服器裡看看效果吧~~

瀏覽器要求支援以下 Feature:

程式碼直接從現有專案移植過來,沒有經過“太多的”測試,寫的很亂,也沒註釋,大家就慢慢看吧。。。重點就在 js 指令碼的 28 行,clipImage 函式中,同學們可以直接跳過去看。

第一步:獲取檔案

HTML5 支援從 input[type=file] 元素中直接獲取檔案資訊,也可以讀取檔案內容。我們用下面程式碼就可以實現:

javascript$('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 裡,這樣本地圖片就顯示出來了。

javascript$('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.naturalHeightimage.naturalWidth 這兩個屬性來獲取圖片原始尺寸。

將圖片放置入 canvas 時需要呼叫 drawImage ,這個介面引數比較多,在 MDN 上有詳細的說明。

javascriptdrawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)

因為我們用 canvas 只是用於裁剪圖片的,所以需要新建一個 canvas 讓它的尺寸和裁剪之後圖片的尺寸相等,此時 canvas 就相當與我們的裁剪框。運用這個函式還可以將大圖縮放成小圖,同學們自己研究吧。

javascript// 以下四個引數由第三步獲得
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 轉換一下。總之這裡挺麻煩的。。

javascriptvar 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 裡請求。

javascriptvar fd=new FormData();

fd.append('file',blob);
$.ajax({
    url:"your.server.com",
    type:"POST",
    data:fd,
    success:function(){}
});

然後你伺服器裡應該就可以收到這個檔案了~