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.naturalHeight
和 image.naturalWidth
這兩個屬性來獲取圖片原始尺寸。
將圖片放置入 canvas
時需要呼叫 drawImage
,這個介面引數比較多,在
MDN 上有詳細的說明。
javascript
drawImage(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
轉換一下。總之這裡挺麻煩的。。
javascript
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
裡請求。
javascript
var fd=new FormData(); fd.append('file',blob); $.ajax({ url:"your.server.com", type:"POST", data:fd, success:function(){} });
然後你伺服器裡應該就可以收到這個檔案了~