通過(guò)Canvas及File API縮放并上傳圖片的方法?這個(gè)問(wèn)題可能是我們?nèi)粘W(xué)習(xí)或工作經(jīng)常見(jiàn)到的。希望通過(guò)這個(gè)問(wèn)題能讓你收獲頗深。下面是小編給大家?guī)?lái)的參考內(nèi)容,讓我們一起來(lái)看看吧!
創(chuàng)新互聯(lián)專注于延安企業(yè)網(wǎng)站建設(shè),響應(yīng)式網(wǎng)站,商城系統(tǒng)網(wǎng)站開(kāi)發(fā)。延安網(wǎng)站建設(shè)公司,為延安等地區(qū)提供建站服務(wù)。全流程按需策劃設(shè)計(jì),專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)
創(chuàng)建一個(gè)只管的用戶界面,并允許你控制圖片的大小。上傳到服務(wù)器端的數(shù)據(jù),并不需要處理enctype為 multi-part/form-data 的情況,僅僅一個(gè)簡(jiǎn)單的POST表單處理程序就可以了. 好了,下面附上完整的代碼示例
示例地址:Canvas Resize Demo
原文作者:Dr. Tom Trenka
原文日期: 2013年8月6日
翻譯日期: 2013年8月8日
Tom Trenka 能為"我"的博客寫一篇文章,對(duì)我來(lái)說(shuō)是一個(gè)巨大的榮譽(yù)。Tom是Dojo框架的最初貢獻(xiàn)者之一,也是我在SitePen公司的良師益友.我見(jiàn)證了他最頂級(jí)的天才能力,并且他總是第一個(gè)以前瞻性的解決方案預(yù)見(jiàn)了很多棘手的問(wèn)題。他總是站在局外思考,打破常規(guī)但卻又堅(jiān)實(shí)可靠地解決邊緣問(wèn)題。本文就是一個(gè)完美的例證。
最近我總是被問(wèn)道要?jiǎng)?chuàng)造一個(gè)用戶接口API,允許用戶上傳圖片到服務(wù)器上(伴隨其他的事情),并能在我們公司提供支持的大量網(wǎng)站的客戶端上使用。通常來(lái)說(shuō)這都是很容易的事情——?jiǎng)?chuàng)建一個(gè)form表單,添加一個(gè)file類型的input輸入框,讓用戶從電腦里選擇圖片,并在form標(biāo)簽上設(shè)置enctype="multipart/form-data"表單屬性,然后上傳即可。非常簡(jiǎn)單,不是嗎?事實(shí)上,這里有一個(gè)足夠簡(jiǎn)單的例子;點(diǎn)擊進(jìn)入
但是如果你想要通過(guò)某些方式預(yù)先處理一下圖片再上傳,那該怎么辦?比如說(shuō),你必須先壓縮圖片尺寸,或者需要圖片只能是某些種類的格式,如 png 或者jpg,你怎么辦?
用canvas來(lái)解決!
Canvas簡(jiǎn)介
canvas 是一個(gè)HTML5新增的DOM元素,允許用戶在頁(yè)面上直接地繪制圖形,通常是使用JavaScript.而不同的格式標(biāo)準(zhǔn)也是不同的,比如SVG是光柵API(raster API) 而VML卻是向量API(vector API).可以考慮使用Adobe Illustrator(矢量圖)作圖與使用 Adobe Photoshop (光柵圖)作圖的區(qū)別。
在canvas(畫(huà)布)上能做的事情就是讀取和渲染圖像,并且允許你通過(guò)JavaScript操縱圖像數(shù)據(jù)。已經(jīng)有很多現(xiàn)存的文章來(lái)為你演示基本的圖像處理——主要關(guān)注與各種不同的圖像過(guò)濾技術(shù)( image filtering techniques)——但我們需要的僅僅是縮放圖片并轉(zhuǎn)換到特定的文件格式,而canvas完全可以做到這些事情。
我們假定的需求,比如圖像高度不超過(guò)100像素,不管原始圖像有多高?;镜拇a如下所示:
// 參數(shù),最大高度 var MAX_HEIGHT = 100; // 渲染 function render(src){ // 創(chuàng)建一個(gè) Image 對(duì)象 var image = new Image(); // 綁定 load 事件處理器,加載完成后執(zhí)行 image.onload = function(){ // 獲取 canvas DOM 對(duì)象 var canvas = document.getElementById("myCanvas"); // 如果高度超標(biāo) if(image.height > MAX_HEIGHT) { // 寬度等比例縮放 *= image.width *= MAX_HEIGHT / image.height; image.height = MAX_HEIGHT; } // 獲取 canvas的 2d 環(huán)境對(duì)象, // 可以理解Context是管理員,canvas是房子 var ctx = canvas.getContext("2d"); // canvas清屏 ctx.clearRect(0, 0, canvas.width, canvas.height); // 重置canvas寬高 canvas.width = image.width; canvas.height = image.height; // 將圖像繪制到canvas上 ctx.drawImage(image, 0, 0, image.width, image.height); // !!! 注意,image 沒(méi)有加入到 dom之中 }; // 設(shè)置src屬性,瀏覽器會(huì)自動(dòng)加載。 // 記住必須先綁定事件,才能設(shè)置src屬性,否則會(huì)出同步問(wèn)題。 image.src = src; };
在上面的例子中,你可以使用canvas 的 toDataURL() 方法獲取圖像的 Base64編碼的值(可以類似理解為16進(jìn)制字符串,或者二進(jìn)制數(shù)據(jù)流).
注意: canvas 的 toDataURL() 獲取的URL以字符串開(kāi)頭,有22個(gè)無(wú)用的數(shù)據(jù) "data:image/png;base64,",需要在客戶端或者服務(wù)端進(jìn)行過(guò)濾.
原則上只要瀏覽器支持,URL地址的長(zhǎng)度是沒(méi)有限制的,而1024的長(zhǎng)度限制,是老一代IE所獨(dú)有的。
請(qǐng)問(wèn),如何獲取我們需要的圖像呢?
好孩子,很高興你能這么問(wèn)。你并不能通過(guò)File 輸入框來(lái)直接處理,你從這個(gè)文件輸入框元素所能獲取的僅僅是用戶所選擇文件的path路徑。按照常規(guī)想象,你可以通過(guò)這個(gè)path路徑信息來(lái)加載圖像,但是,在瀏覽器里面這是不現(xiàn)實(shí)的。(譯者注:瀏覽器廠商必須保證自己的瀏覽器絕對(duì)安全,才能獲得市場(chǎng),至少避免媒體的攻擊,如果允許這樣做,那惡意網(wǎng)址可以通過(guò)拼湊文件路徑來(lái)嘗試獲取某些敏感信息).
為了實(shí)現(xiàn)這個(gè)需求,我們可以使用HTML5的File API 來(lái)讀取用戶磁盤上的文件,并用這個(gè)file來(lái)作為圖像的源(src,source).
File API簡(jiǎn)介
新的File API接口是在不違背任何安全沙盒規(guī)則下,讀取和列出用戶文件目錄的一個(gè)途徑—— 通過(guò)沙盒(sandbox)限制,惡意網(wǎng)站并不能將病毒寫入用戶磁盤,當(dāng)然更不能執(zhí)行。
我們要使用的文件讀取對(duì)象叫做 FileReader,FileReader允許開(kāi)發(fā)者讀取文件的內(nèi)容(具體瀏覽器的實(shí)現(xiàn)方式可能大不相同)。
假設(shè)我們已經(jīng)獲取了圖像文件的path路徑,那么依賴前面的代碼,使用FileReader來(lái)加載和渲染圖像就變得很容易了:
// 加載 圖像文件(url路徑) function loadImage(src){ // 過(guò)濾掉 非 image 類型的文件 if(!src.type.match(/image.*/)){ if(window.console){ console.log("選擇的文件類型不是圖片: ", src.type); } else { window.confirm("只能選擇圖片文件"); } return; } // 創(chuàng)建 FileReader 對(duì)象 并調(diào)用 render 函數(shù)來(lái)完成渲染. var reader = new FileReader(); // 綁定load事件自動(dòng)回調(diào)函數(shù) reader.onload = function(e){ // 調(diào)用前面的 render 函數(shù) render(e.target.result); }; // 讀取文件內(nèi)容 reader.readAsDataURL(src); };
請(qǐng)問(wèn),如何獲取文件呢?
小白兔,要有耐心!我們的下一步就是獲取文件,當(dāng)然有好多方法可以實(shí)現(xiàn)啦。例如:你可以用文本框讓用戶輸入文件路徑,但很顯然大多數(shù)用戶都不是開(kāi)發(fā)者,對(duì)輸入什么值根本就不了解.
為了用戶使用方便,我們采用 Drag and Drop API接口。
使用 Drag and Drop API
拖拽接口(Drag and Drop)非常簡(jiǎn)單——在大多數(shù)的DOM元素上,你都可以通過(guò)綁定事件處理器來(lái)實(shí)現(xiàn). 只要用戶從磁盤上拖動(dòng)一個(gè)文件到dom對(duì)象上并放開(kāi)鼠標(biāo),那我們就可以讀取這個(gè)文件。代碼如下:
function init(){ // 獲取DOM元素對(duì)象 var target = document.getElementById("drop-target"); // 阻止 dragover(拖到DOM元素上方) 事件傳遞 target.addEventListener("dragover", function(e){e.preventDefault();}, true); // 拖動(dòng)并放開(kāi)鼠標(biāo)的事件 target.addEventListener("drop", function(e){ // 阻止默認(rèn)事件,以及事件傳播 e.preventDefault(); // 調(diào)用前面的加載圖像 函數(shù),參數(shù)為dataTransfer對(duì)象的第一個(gè)文件 loadImage(e.dataTransfer.files[0]); }, true); var setheight = document.getElementById("setheight"); var maxheight = document.getElementById("maxheight"); setheight.addEventListener("click", function(e){ // var value = maxheight.value; if(/^\d+$/.test(value)){ MAX_HEIGHT = parseInt(value); } e.preventDefault(); },true); var btnsend = document.getElementById("btnsend"); btnsend.addEventListener("click", function(e){ // sendImage(); },true); };
我們還可以做一些其他的處理,比如顯示預(yù)覽圖。但如果不想壓縮圖片的話,那很可能沒(méi)什么用。我們將采用Ajax通過(guò)HTTP 的post方式上傳圖片數(shù)據(jù)。下面的例子是使用Dojo框架來(lái)完成請(qǐng)求的,當(dāng)然你也可以采用其他的Ajax技術(shù)來(lái)實(shí)現(xiàn).。
Dojo 代碼如下:
// 譯者并不懂Dojo,所以將在后面附上jQuery的實(shí)現(xiàn) // Remember that DTK 1.7+ is AMD! require(["dojo/request"], function(request){ // 設(shè)置請(qǐng)求URL,參數(shù),以及回調(diào)。 request.post("image-handler.php", { data: { imageName: "myImage.png", imageData: encodeURIComponent(document.getElementById("canvas").toDataURL("image/png")) } }).then(function(text){ console.log("The server returned: ", text); }); });
jQuery 實(shí)現(xiàn)如下:
// 上傳圖片,jQuery版 function sendImage(){ // 獲取 canvas DOM 對(duì)象 var canvas = document.getElementById("myCanvas"); // 獲取Base64編碼后的圖像數(shù)據(jù),格式是字符串 // "data:image/png;base64,"開(kāi)頭,需要在客戶端或者服務(wù)器端將其去掉,后面的部分可以直接寫入文件。 var dataurl = canvas.toDataURL("image/png"); // 為安全 對(duì)URI進(jìn)行編碼 // data%3Aimage%2Fpng%3Bbase64%2C 開(kāi)頭 var imagedata = encodeURIComponent(dataurl); //var url = $("#form").attr("action"); // 1. 如果form表單不好處理,可以使用某個(gè)hidden隱藏域來(lái)設(shè)置請(qǐng)求地址 // var url = $("input[name='action']").val(); // 2. 也可以直接用某個(gè)dom對(duì)象的屬性來(lái)獲取 // // var url = $("#imageaction").attr("action"); // 因?yàn)槭莝tring,所以服務(wù)器需要對(duì)數(shù)據(jù)進(jìn)行轉(zhuǎn)碼,寫文件操作等。 // 個(gè)人約定,所有http參數(shù)名字全部小寫 console.log(dataurl); //console.log(imagedata); var data = { imagename: "myImage.png", imagedata: imagedata }; jQuery.ajax( { url : url, data : data, type : "POST", // 期待的返回值類型 dataType: "json", complete : function(xhr,result) { //console.log(xhr.responseText); var $tip2 = $("#tip2"); if(!xhr){ $tip2.text('網(wǎng)絡(luò)連接失敗!'); return false; } var text = xhr.responseText; if(!text){ $tip2.text('網(wǎng)絡(luò)錯(cuò)誤!'); return false; } var json = eval("("+text+")"); if(!json){ $tip2.text('解析錯(cuò)誤!'); return false; } else { $tip2.text(json.message); } //console.dir(json); //console.log(xhr.responseText); } }); };
OK,搞定!你還需要做的,就是創(chuàng)建一個(gè)只管的用戶界面,并允許你控制圖片的大小。上傳到服務(wù)器端的數(shù)據(jù),并不需要處理enctype為 multi-part/form-data 的情況,僅僅一個(gè)簡(jiǎn)單的POST表單處理程序就可以了.
好了,下面附上完整的代碼示例:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %>通過(guò)Canvas及File API縮放并上傳圖片
通過(guò)Canvas及File API縮放并上傳圖片
從文件夾拖動(dòng)一張照片到下方的盒子里, canvas 和 JavaScript將會(huì)自動(dòng)的進(jìn)行縮放.
拖動(dòng)圖片文件到這里...
縮略圖:
服務(wù)端頁(yè)面,receive.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@page import="sun.misc.BASE64Decoder"%> <%@page import="java.io.*"%> <%@page import="org.springframework.web.util.UriComponents"%> <%@page import="java.net.URLDecoder"%> <%! // 本文件:/receive.jsp // 圖片存放路徑 String photoPath = "D:/blog/upload/photo/"; File photoPathFile = new File(photoPath); // references: http://blog.csdn.net/remote_roamer/article/details/2979822 private boolean saveImageToDisk(byte[] data,String imageName) throws IOException{ int len = data.length; // // 寫入到文件 FileOutputStream outputStream = new FileOutputStream(new File(photoPathFile,imageName)); outputStream.write(data); outputStream.flush(); outputStream.close(); // return true; } private byte[] decode(String imageData) throws IOException{ BASE64Decoder decoder = new BASE64Decoder(); byte[] data = decoder.decodeBuffer(imageData); for(int i=0;i<% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <% //如果是IE,那么需要設(shè)置為text/html,否則會(huì)彈框下載 //response.setContentType("text/html;charset=UTF-8"); response.setContentType("application/json;charset=UTF-8"); // String imageName = request.getParameter("imagename"); String imageData = request.getParameter("imagedata"); int success = 0; String message = ""; if(null == imageData || imageData.length() < 100){ // 數(shù)據(jù)太短,明顯不合理 message = "上傳失敗,數(shù)據(jù)太短或不存在"; } else { // 去除開(kāi)頭不合理的數(shù)據(jù) imageData = imageData.substring(30); imageData = URLDecoder.decode(imageData,"UTF-8"); //System.out.println(imageData); byte[] data = decode(imageData); int len = data.length; int len2 = imageData.length(); if(null == imageName || imageName.length() < 1){ imageName = System.currentTimeMillis()+".png"; } saveImageToDisk(data,imageName); // success = 1; message = "上傳成功,參數(shù)長(zhǎng)度:"+len2+"字符,解析文件大小:"+len+"字節(jié)"; } // 后臺(tái)打印 System.out.println("message="+message); %> { "message": "<%=message %>", "success": <%=success %> }
感謝各位的閱讀!看完上述內(nèi)容,你們對(duì)通過(guò)Canvas及File API縮放并上傳圖片的方法大概了解了嗎?希望文章內(nèi)容對(duì)大家有所幫助。如果想了解更多相關(guān)文章內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。