首頁 >web前端 >H5教程 >如何透過Canvas及File API縮放並上傳圖片

如何透過Canvas及File API縮放並上傳圖片

不言
不言原創
2018-06-22 14:57:391794瀏覽

這篇文章主要介紹了關於如何透過Canvas及File API縮放並上傳圖片,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下

創建一個只管的用戶介面,並允許你控制圖片的大小。上傳到伺服器端的數據,並不需要處理enctype為 multi-part/form-data 的情況,僅僅一個簡單的POST表單處理程序就可以了. 好了,下面附上完整的程式碼範例 範例網址:Canvas Resize Demo
原文作者:Dr. Tom Trenka
原文日期: 2013年8月6日
翻譯日期: 2013年8月8日 

Tom Trenka 能為"我"的部落格寫一篇文章,對我來說是一個巨大的榮譽。 Tom是Dojo框架的最初貢獻者之一,也是我在SitePen公司的良師益友.我見證了他最頂級的天才能力,並且他總是第一個以前瞻性的解決方案預見了很多棘手的問題。他總是站在局外思考,打破常規但卻堅實可靠地解決邊緣問題。本文就是一個完美的例子。
最近我總是被問道要創造一個用戶介面API,允許用戶上傳圖片到伺服器上(伴隨其他的事情),並能在我們公司提供支援的大量網站的客戶端上使用。通常來說這都是很容易的事情-建立一個form表單,加入一個file類型的input輸入框,讓使用者從電腦裡選擇圖片,並在form標籤上設定enctype="multipart/form-data"表單屬性,然後上傳即可。非常簡單,不是嗎?事實上,這裡有一個足夠簡單的例子;點擊進入
但是如果你想要透過某些方式預先處理一下圖片再上傳,那該怎麼辦?比方說,你必須先壓縮圖片尺寸,或是需要圖片只能是某些種類的格式,如 png 或jpg,你怎麼辦?
用canvas來解決! 

Canvas簡介
canvas 是一個HTML5新增的DOM元素,允許使用者在頁面上直接地繪製圖形,通常是使用JavaScript.而不同的格式標準也是不同的,例如SVG是光柵API(raster API) 而VML卻是向量API(vector API).可以考慮使用Adobe Illustrator(向量圖)作圖與使用Adobe Photoshop (光柵圖)作圖的區別。 

在canvas(畫布)上能做的事情就是讀取和渲染影像,並且允許你透過JavaScript操縱影像資料。已經有很多現存的文章來為你演示基本的圖像處理——主要關注與各種不同的圖像過濾技術( image filtering techniques)——但我們需要的僅僅是縮放圖片並轉換到特定的文件格式,而canvas完全可以做到這些事。 

我們假定的需求,例如影像高度不超過100像素,不管原始影像有多高。基本的程式碼如下所示:

// 参数,最大高度 
var MAX_HEIGHT = 100; 
// 渲染 
function render(src){ 
// 创建一个 Image 对象 
var image = new Image(); 
// 绑定 load 事件处理器,加载完成后执行 
image.onload = function(){ 
// 获取 canvas DOM 对象 
var canvas = document.getElementById("myCanvas"); 
// 如果高度超标 
if(image.height > MAX_HEIGHT) { 
// 宽度等比例缩放 *= 
image.width *= MAX_HEIGHT / image.height; 
image.height = MAX_HEIGHT; 
} 
// 获取 canvas的 2d 环境对象, 
// 可以理解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 没有加入到 dom之中 
}; 
// 设置src属性,浏览器会自动加载。 
// 记住必须先绑定事件,才能设置src属性,否则会出同步问题。 
image.src = src; 
};

在上面的範例中,你可以使用canvas 的toDataURL() 方法取得影像的Base64編碼的值(可以類似理解為16進位字串,或二進位數據流).
注意: canvas 的toDataURL() 取得的URL以字串開頭,有22個無用的資料"data:image/png;base64,",需要在客戶端或服務端進行過濾.
原則上只要瀏覽器支持,URL位址的長度是沒有限制的,而1024的長度限制,是老一代IE所獨有的。 

請問,如何取得我們需要的圖像?
好孩子,很高興你能這麼問。你並不能透過File 輸入框來直接處理,你從這個檔案輸入框元素所能取得的只是使用者所選檔案的path路徑。按照常規想像,你可以透過這個path路徑資訊來載入圖像,但是,在瀏覽器裡面這是不切實際的。 (譯者註:瀏覽器廠商必須保證自己的瀏覽器絕對安全,才能獲得市場,至少避免媒體的攻擊,如果允許這樣做,那惡意網址可以通過拼湊文件路徑來嘗試獲取某些敏感信息).
為了實現這個需求,我們可以使用HTML5的File API 來讀取用戶磁碟上的文件,並用這個file來作為圖像的來源(src,source). 

File API簡介
新的File API介面是在不違反任何安全沙盒規則下,讀取和列出使用者檔案目錄的一個途徑- 透過沙盒(sandbox)限制,惡意網站並不能將病毒寫入用戶磁碟,當然更不能執行。
我們要使用的檔案讀取物件叫做 FileReader,FileReader允許開發者讀取檔案的內容(具體瀏覽器的實作方式可能大不相同)。 

假設我們已經取得了映像檔的path路徑,那麼依賴前面的程式碼,使用FileReader來載入和渲染影像就變得很容易了:

// 加载 图像文件(url路径) 
function loadImage(src){ 
// 过滤掉 非 image 类型的文件 
if(!src.type.match(/image.*/)){ 
if(window.console){ 
console.log("选择的文件类型不是图片: ", src.type); 
} else { 
window.confirm("只能选择图片文件"); 
} 
return; 
} 
// 创建 FileReader 对象 并调用 render 函数来完成渲染. 
var reader = new FileReader(); 
// 绑定load事件自动回调函数 
reader.onload = function(e){ 
// 调用前面的 render 函数 
render(e.target.result); 
}; 
// 读取文件内容 
reader.readAsDataURL(src); 
};

请问,如何获取文件呢?
小白兔,要有耐心!我们的下一步就是获取文件,当然有好多方法可以实现啦。例如:你可以用文本框让用户输入文件路径,但很显然大多数用户都不是开发者,对输入什么值根本就不了解.
为了用户使用方便,我们采用 Drag and Drop API接口。 

使用 Drag and Drop API
拖拽接口(Drag and Drop)非常简单——在大多数的DOM元素上,你都可以通过绑定事件处理器来实现. 只要用户从磁盘上拖动一个文件到dom对象上并放开鼠标,那我们就可以读取这个文件。代码如下: 

function init(){ 
// 获取DOM元素对象 
var target = document.getElementById("drop-target"); 
// 阻止 dragover(拖到DOM元素上方) 事件传递 
target.addEventListener("dragover", function(e){e.preventDefault();}, true); 
// 拖动并放开鼠标的事件 
target.addEventListener("drop", function(e){ 
// 阻止默认事件,以及事件传播 
e.preventDefault(); 
// 调用前面的加载图像 函数,参数为dataTransfer对象的第一个文件 
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); 
};

我们还可以做一些其他的处理,比如显示预览图。但如果不想压缩图片的话,那很可能没什么用。我们将采用Ajax通过HTTP 的post方式上传图片数据。下面的例子是使用Dojo框架来完成请求的,当然你也可以采用其他的Ajax技术来实现.。

Dojo 代码如下:

// 译者并不懂Dojo,所以将在后面附上jQuery的实现 
// Remember that DTK 1.7+ is AMD! 
require(["dojo/request"], function(request){ 
// 设置请求URL,参数,以及回调。 
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 实现如下:

// 上传图片,jQuery版 
function sendImage(){ 
// 获取 canvas DOM 对象 
var canvas = document.getElementById("myCanvas"); 
// 获取Base64编码后的图像数据,格式是字符串 
// "data:image/png;base64,"开头,需要在客户端或者服务器端将其去掉,后面的部分可以直接写入文件。 
var dataurl = canvas.toDataURL("image/png"); 
// 为安全 对URI进行编码 
// data%3Aimage%2Fpng%3Bbase64%2C 开头 
var imagedata = encodeURIComponent(dataurl); 
//var url = $("#form").attr("action"); 
// 1. 如果form表单不好处理,可以使用某个hidden隐藏域来设置请求地址 
// <input type="hidden" name="action" value="receive.jsp" /> 
var url = $("input[name='action']").val(); 
// 2. 也可以直接用某个dom对象的属性来获取 
// <input id="imageaction" type="hidden" action="receive.jsp"> 
// var url = $("#imageaction").attr("action"); 
// 因为是string,所以服务器需要对数据进行转码,写文件操作等。 
// 个人约定,所有http参数名字全部小写 
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('网络连接失败!'); 
return false; 
} 
var text = xhr.responseText; 
if(!text){ 
$tip2.text('网络错误!'); 
return false; 
} 
var json = eval("("+text+")"); 
if(!json){ 
$tip2.text('解析错误!'); 
return false; 
} else { 
$tip2.text(json.message); 
} 
//console.dir(json); 
//console.log(xhr.responseText); 
} 
}); 
};

OK,搞定!你还需要做的,就是创建一个只管的用户界面,并允许你控制图片的大小。上传到服务器端的数据,并不需要处理enctype为 multi-part/form-data 的情况,仅仅一个简单的POST表单处理程序就可以了.
好了,下面附上完整的代码示例: 

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> 
<% 
String path = request.getContextPath(); 
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; 
%> 
 
 
 
通过Canvas及File API缩放并上传图片 
 
 
 
 
 
 
<script> 
// 参数,最大高度 
var MAX_HEIGHT = 100; 
// 渲染 
function render(src){ 
// 创建一个 Image 对象 
var image = new Image(); 
// 绑定 load 事件处理器,加载完成后执行 
image.onload = function(){ 
// 获取 canvas DOM 对象 
var canvas = document.getElementById(&quot;myCanvas&quot;); 
// 如果高度超标 
if(image.height &gt; MAX_HEIGHT) { 
// 宽度等比例缩放 *= 
image.width *= MAX_HEIGHT / image.height; 
image.height = MAX_HEIGHT; 
} 
// 获取 canvas的 2d 环境对象, 
// 可以理解Context是管理员,canvas是房子 
var ctx = canvas.getContext(&quot;2d&quot;); 
// 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 没有加入到 dom之中 
}; 
// 设置src属性,浏览器会自动加载。 
// 记住必须先绑定事件,才能设置src属性,否则会出同步问题。 
image.src = src; 
}; 
// 加载 图像文件(url路径) 
function loadImage(src){ 
// 过滤掉 非 image 类型的文件 
if(!src.type.match(/image.*/)){ 
if(window.console){ 
console.log(&quot;选择的文件类型不是图片: &quot;, src.type); 
} else { 
window.confirm(&quot;只能选择图片文件&quot;); 
} 
return; 
} 
// 创建 FileReader 对象 并调用 render 函数来完成渲染. 
var reader = new FileReader(); 
// 绑定load事件自动回调函数 
reader.onload = function(e){ 
// 调用前面的 render 函数 
render(e.target.result); 
}; 
// 读取文件内容 
reader.readAsDataURL(src); 
}; 
// 上传图片,jQuery版 
function sendImage(){ 
// 获取 canvas DOM 对象 
var canvas = document.getElementById(&quot;myCanvas&quot;); 
// 获取Base64编码后的图像数据,格式是字符串 
// &quot;data:image/png;base64,&quot;开头,需要在客户端或者服务器端将其去掉,后面的部分可以直接写入文件。 
var dataurl = canvas.toDataURL(&quot;image/png&quot;); 
// 为安全 对URI进行编码 
// data%3Aimage%2Fpng%3Bbase64%2C 开头 
var imagedata = encodeURIComponent(dataurl); 
//var url = $(&quot;#form&quot;).attr(&quot;action&quot;); 
// 1. 如果form表单不好处理,可以使用某个hidden隐藏域来设置请求地址 
// &lt;input type=&quot;hidden&quot; name=&quot;action&quot; value=&quot;receive.jsp&quot; /&gt; 
var url = $(&quot;input[name='action']&quot;).val(); 
// 2. 也可以直接用某个dom对象的属性来获取 
// &lt;input id=&quot;imageaction&quot; type=&quot;hidden&quot; action=&quot;receive.jsp&quot;&gt; 
// var url = $(&quot;#imageaction&quot;).attr(&quot;action&quot;); 
// 因为是string,所以服务器需要对数据进行转码,写文件操作等。 
// 个人约定,所有http参数名字全部小写 
console.log(dataurl); 
//console.log(imagedata); 
var data = { 
imagename: &quot;myImage.png&quot;, 
imagedata: imagedata 
}; 
jQuery.ajax( { 
url : url, 
data : data, 
type : &quot;POST&quot;, 
// 期待的返回值类型 
dataType: &quot;json&quot;, 
complete : function(xhr,result) { 
//console.log(xhr.responseText); 
var $tip2 = $(&quot;#tip2&quot;); 
if(!xhr){ 
$tip2.text('网络连接失败!'); 
return false; 
} 
var text = xhr.responseText; 
if(!text){ 
$tip2.text('网络错误!'); 
return false; 
} 
var json = eval(&quot;(&quot;+text+&quot;)&quot;); 
if(!json){ 
$tip2.text('解析错误!'); 
return false; 
} else { 
$tip2.text(json.message); 
} 
//console.dir(json); 
//console.log(xhr.responseText); 
} 
}); 
}; 
function init(){ 
// 获取DOM元素对象 
var target = document.getElementById(&quot;drop-target&quot;); 
// 阻止 dragover(拖到DOM元素上方) 事件传递 
target.addEventListener(&quot;dragover&quot;, function(e){e.preventDefault();}, true); 
// 拖动并放开鼠标的事件 
target.addEventListener(&quot;drop&quot;, function(e){ 
// 阻止默认事件,以及事件传播 
e.preventDefault(); 
// 调用前面的加载图像 函数,参数为dataTransfer对象的第一个文件 
loadImage(e.dataTransfer.files[0]); 
}, true); 
var setheight = document.getElementById(&quot;setheight&quot;); 
var maxheight = document.getElementById(&quot;maxheight&quot;); 
setheight.addEventListener(&quot;click&quot;, function(e){ 
// 
var value = maxheight.value; 
if(/^\d+$/.test(value)){ 
MAX_HEIGHT = parseInt(value); 
} 
e.preventDefault(); 
},true); 
var btnsend = document.getElementById(&quot;btnsend&quot;); 
btnsend.addEventListener(&quot;click&quot;, function(e){ 
// 
sendImage(); 
},true); 
}; 
window.addEventListener("DOMContentLoaded", function() { 
// 
init(); 
},false); 
</script> 
 
 

 

通过Canvas及File API缩放并上传图片

 

从文件夹拖动一张照片到下方的盒子里, canvas 和 JavaScript将会自动的进行缩放.

 

       

 

 

拖动图片文件到这里...

 

 

    

 

 

缩略图:

 

   

 

 

   

服务端页面,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<data.length;++i) 
{ 
if(data[i]<0) 
{ 
//调整异常数据 
data[i]+=256; 
} 
} 
// 
return data; 
} 
%> 
<% 
String path = request.getContextPath(); 
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; 
%> 
<% 
//如果是IE,那么需要设置为text/html,否则会弹框下载 
//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){ 
// 数据太短,明显不合理 
message = "上传失败,数据太短或不存在"; 
} else { 
// 去除开头不合理的数据 
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 = "上传成功,参数长度:"+len2+"字符,解析文件大小:"+len+"字节"; 
} 
// 后台打印 
System.out.println("message="+message); 
%> 
{ 
"message": "<%=message %>", 
"success": <%=success %> 
}

以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!

相关推荐:

借助toDataURL实现将HTML5 Canvas的内容保存为图片

以上是如何透過Canvas及File API縮放並上傳圖片的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn