溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何通過(guò)Canvas及File API縮放并上傳圖片

發(fā)布時(shí)間:2021-09-17 17:56:45 來(lái)源:億速云 閱讀:141 作者:chen 欄目:web開發(fā)

這篇文章主要講解了“如何通過(guò)Canvas及File API縮放并上傳圖片”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“如何通過(guò)Canvas及File API縮放并上傳圖片”吧!



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(畫布)上能做的事情就是讀取和渲染圖像,并且允許你通過(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以字符串開頭,有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允許開發(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ù)用戶都不是開發(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ì)象上并放開鼠標(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)并放開鼠標(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,"開頭,需要在客戶端或者服務(wù)器端將其去掉,后面的部分可以直接寫入文件。
var dataurl = canvas.toDataURL("image/png");
// 為安全 對(duì)URI進(jìn)行編碼
// data%3Aimage%2Fpng%3Bbase64%2C 開頭
var imagedata = encodeURIComponent(dataurl);
//var url = $("#form").attr("action");
// 1. 如果form表單不好處理,可以使用某個(gè)hidden隱藏域來(lái)設(shè)置請(qǐng)求地址
// <input type="hidden" name="action" value="receive.jsp" />
var url = $("input[name='action']").val();
// 2. 也可以直接用某個(gè)dom對(duì)象的屬性來(lái)獲取
// <input id="imageaction" type="hidden" action="receive.jsp">
// 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+"/";
%>
<!DOCTYPE html>
<html>
<head>
<title>通過(guò)Canvas及File API縮放并上傳圖片</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="Canvas,File,Image">
<meta http-equiv="description" content="2013年8月8日,renfufei@qq.com">
<script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script>
// 參數(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;
};
// 加載 圖像文件(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);
};
// 上傳圖片,jQuery版
function sendImage(){
// 獲取 canvas DOM 對(duì)象
var canvas = document.getElementById("myCanvas");
// 獲取Base64編碼后的圖像數(shù)據(jù),格式是字符串
// "data:image/png;base64,"開頭,需要在客戶端或者服務(wù)器端將其去掉,后面的部分可以直接寫入文件。
var dataurl = canvas.toDataURL("image/png");
// 為安全 對(duì)URI進(jìn)行編碼
// data%3Aimage%2Fpng%3Bbase64%2C 開頭
var imagedata = encodeURIComponent(dataurl);
//var url = $("#form").attr("action");
// 1. 如果form表單不好處理,可以使用某個(gè)hidden隱藏域來(lái)設(shè)置請(qǐng)求地址
// <input type="hidden" name="action" value="receive.jsp" />
var url = $("input[name='action']").val();
// 2. 也可以直接用某個(gè)dom對(duì)象的屬性來(lái)獲取
// <input id="imageaction" type="hidden" action="receive.jsp">
// 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);
}
});
};
function init(){
// 獲取DOM元素對(duì)象
var target = document.getElementById("drop-target");
// 阻止 dragover(拖到DOM元素上方) 事件傳遞
target.addEventListener("dragover", function(e){e.preventDefault();}, true);
// 拖動(dòng)并放開鼠標(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);
};
window.addEventListener("DOMContentLoaded", function() {
//
init();
},false);
</script>
</head>
<body>
<div>
<h2>通過(guò)Canvas及File API縮放并上傳圖片</h2>
<p>從文件夾拖動(dòng)一張照片到下方的盒子里, canvas 和 JavaScript將會(huì)自動(dòng)的進(jìn)行縮放.</p>
<div>
<input type="text" id="maxheight" value="100"/>
<button id="setheight">設(shè)置圖片最大高度</button>
<input type="hidden" name="action" value="receive.jsp" />
</div>
<div id="preview-row">
<div id="drop-target" >拖動(dòng)圖片文件到這里...</div>
<div>
<div>
<button id="btnsend"> 上 傳 </button> <span id="tip2" ></span>
</div>
</div>
<div><h5>縮略圖:</h5></div>
<div id="preview" >
<canvas id="myCanvas"></canvas>
</div>
</div>
</div>
</body>
</html>


服務(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<data.length;++i)
{
if(data[i]<0)
{
//調(diào)整異常數(shù)據(jù)
data[i]+=256;
}
}
//
return data;
}
%>
<%
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 {
// 去除開頭不合理的數(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 %>
}

感謝各位的閱讀,以上就是“如何通過(guò)Canvas及File API縮放并上傳圖片”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)如何通過(guò)Canvas及File API縮放并上傳圖片這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI