如何用CropBox實現頭像裁剪並與java后台交互
參考網站:https://developer.mozilla.org/zh-CN/docs/Web/API/Blob
參考:
http://blog.csdn.net/u013160024/article/details/51849732
http://www.cnblogs.com/shinefon-2-2/p/5901330.html
http://www.cnblogs.com/hhhyaaon/p/5928152.html
主流的前端jQuery 圖像裁剪插件有Jcrop和CropBox,前者是將原圖和需要裁剪的參數(裁剪的各點坐標,旋轉角度等)傳到后台,然后由后台完成實際的裁剪和后續操作。
CropBox實現功能相對較少,但操作更簡單,它的原理是:
將裁減后的圖片通過base64編碼,然后轉化為blob格式發送到服務器,服務器完成解碼即可,官網介紹可以看github上的說明和Demo
核心js函數只有兩個:
getDataURL 將裁剪后的圖片簡單以base64編碼后的結果,用於實時預覽,當然也可以將它直接傳到服務器,然后解碼為png格式
getBlob 上傳圖片為Blob格式
首先貼出兩個函數的源碼:
getDataURL: function ()
{
var width = this.thumbBox.width(),
height = this.thumbBox.height(),
canvas = document.createElement("canvas"),
dim = el.css('background-position').split(' '),
size = el.css('background-size').split(' '),
dx = parseInt(dim[0]) - el.width()/2 + width/2,
dy = parseInt(dim[1]) - el.height()/2 + height/2,
dw = parseInt(size[0]),
dh = parseInt(size[1]),
sh = parseInt(this.image.height),
sw = parseInt(this.image.width);
canvas.width = width;
canvas.height = height;
var context = canvas.getContext("2d");
context.drawImage(this.image, 0, 0, sw, sh, dx, dy, dw, dh);
var imageData = canvas.toDataURL('image/png');
return imageData;
},
getBlob: function()
{
var imageData = this.getDataURL();
var b64 = imageData.replace('data:image/png;base64,','');
var binary = atob(b64);
var array = [];
for (var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], {type: 'image/png'});
},
1. Data URIs方式
主要利用了HTMLCanvasElement.toDataURL()方法,HTMLCanvasElement.toDataURL() 方法返回一個包含圖片展示的 data URI 。可以使用 type 參數其類型,默認為 PNG 格式。圖片的分辨率為96dpi。
語法:
canvas.toDataURL(type, encoderOptions);
參數:
- type 可選
圖片格式,默認為 image/png - encoderOptions 可選
在指定圖片格式為 image/jpeg 或 image/webp的情況下,可以從 0 到 1 的區間內選擇圖片的質量。如果超出取值范圍,將會使用默認值 0.92。其他參數會被忽略。
返回值:
包含 data URI 的DOMString。
比如:
<canvas id="canvas" width="5" height="5"></canvas>
var canvas = document.getElementById("canvas");
var dataURL = canvas.toDataURL();
console.log(dataURL);
// "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNby
// blAAAADElEQVQImWNgoBMAAABpAAFEI8ARAAAAAElFTkSuQmCC"
1.1 Data URIs
1.1.1 什么是URI
統一資源標識符(Uniform Resource Identifier,或URI)是一個用於標識某一互聯網資源名稱的字符串。Web上可用的每種資源 -HTML文檔、圖像、視頻片段、程序等,都由一個統一資源標識符(Uniform Resource Identifier, 簡稱"URI")進行標識。
而URL是uniform resource locator,統一資源定位器,它是一種具體的URI,即URL可以用來標識一個資源,而且還指明了如何locate這個資源。
1.1.2 怎么用
Data URIs的數據格式很簡單,通過RFC 2397的定義,一般格式是:
data:[mime type][;charset=][;base64],
說明:
- data - 指代URI協議
- mime type - 代表數據類型,如png圖片則為image/png,若不說明,默認為text/plain(上面的默認值為那個函數的默認值,這里是這種數據格式的默認值)
- charset - 如果不使用base64,則使用charset指定的字符類
- encoded data - 對應的編碼信息
1.1.3 優缺點
優點:
1.減少HTTP請求數,沒有了TCP連接消耗和同一域名下瀏覽器的並發數限制,這里說的方式是下圖這種將編碼后的圖片放到標簽的url或者src中。
網頁上的圖片資源如果采用http形式的url的話都會額外發送一次請求,網頁發送的http請求次數越多,會造成頁面加載速度越慢。而采用Base64格式的編碼,將圖片轉化為字符串后,圖片文件會隨着html元素一並加載,這樣就可以減少http請求的次數,對於網頁優化是一種比較好的手段。
2.對於小文件會降低帶寬。雖然編碼后數據量會增加,但是卻減少了http頭,當http頭的數據量大於文件編碼的增量,那么就會降低帶寬。
3.采用Base64編碼的圖片是隨着頁面一起加載的,不會造成跨域請求的問題,也不會造成清理圖片緩存的問題。
缺點:
1.無法被重復利用,同一個文檔應用多次同一個內容,則需要重復多次,數據量大量增加,增加了下載時間。
2.不支持數據壓縮,base64編碼大小會增加1/3左右,而urlencode后數據量會增加更多。
3.當我們將一個只有幾KB的圖片轉化為Base64格式編碼,生成的字符串往往會大於幾KB,如果將其寫在一個css文件中,這樣一個css文件的大小會劇增,造成代碼可讀性差不說,還會造成請求傳輸的數據量遞增。
4.如果我們將Base64編碼的圖片存入數據庫中,會造成數據庫數據量的增大,這樣的效果還不如將圖片存至圖片服務器,而只在數據庫中存入url字段。
5.不利於安全軟件的過濾,同時也存在一定的安全隱患。
由於我們會將圖片傳輸到服務器,為減小傳輸數據量,采用了Blob。
2. Blob(二進制大對象)方式
HTMLCanvasElement.toBlob() 方法創造Blob對象,用以展示canvas上的圖片;這個圖片文件可以被緩存或保存到本地,由用戶代理端自行決定。如不特別指明,圖片的類型默認為 image/png,分辨率為96dpi。
語法:
void canvas.toBlob(callback, type, encoderOptions);
參數:
- callback
回調函數,可獲得一個單獨的Blob對象參數。 - type 可選
DOMString類型,指定圖片格式,默認格式為image/png。 - encoderOptions 可選
Number類型,值在0與1之間,當請求圖片格式為image/jpeg或者image/webp時用來指定圖片展示質量。
比如將canvas圖像轉換為文件,當一個內容畫到canvas上時,我們可以將它生成任何一個格式支持的圖片文件。比如,下面的代碼段獲得了id為“canvas”的<canvas>
元素中的圖像,復制成一個PNG圖,在文檔中加入一個新的<img>
元素,這個<img>
元素的源圖就是使用canvas創建的那個圖像:
var canvas = document.getElementById("canvas");
canvas.toBlob(function(blob) {
var newImg = document.createElement("img"),
url = URL.createObjectURL(blob);
newImg.onload = function() {
// no longer need to read the blob so it's revoked(撤銷,刪除)
URL.revokeObjectURL(url);
};
newImg.src = url;
document.body.appendChild(newImg);
});
注意,我們這里創建的是PNG圖片;如果在toBlob()傳入第二個參數,就可以指定圖片格式。例如,生成JPEG格式的圖片:
canvas.toBlob(function(blob){...}, "image/jpeg", 0.95); // JPEG at 95% quality
2.1 new Blob([new Uint8Array(array)], {type: 'image/png'})
當然CropBox並沒有使用toBlob方法,而是直接利用js中的Blob對象類型的構造方法。
語法:
var aBlob = new Blob( array, options );
參數:
- array 是一個由ArrayBuffer, ArrayBufferView, Blob, DOMString 等對象構成的 Array ,或者其他類似對象的混合體,它將會被放進 Blob.
- options 是一個可選的Blob熟悉字典,它可能會指定如下兩種屬性:
type,默認值為 "",它代表了將會被放入到blob中的數組內容的MIME類型。
endings,默認值為"transparent",它代表包含行結束符\n的字符串如何被輸出。 它是以下兩個值中的一個: "native",代表行結束符會被更改為適合宿主操作系統文件系統的慣例,或者 "transparent", 代表會保持blob中保存的結束符不變
比如:
var aFileParts = ['<a id="a"><b id="b">hey!</b></a>']; // an array consisting of a single DOMString
var oMyBlob = new Blob(aFileParts, {type : 'text/html'}); // the blob
用處:
- 大文件分割 (slice() 方法):
slice() 方法接受三個參數,起始偏移量,結束偏移量,還有可選的 mime 類型,然后輪循向后台提交各文件片段,即可實現文件的分片上傳。
3 Base64編碼
Base64編碼本質上是一種將二進制數據轉成文本數據的方案。對於非二進制數據,是先將其轉換成二進制形式,然后每連續6比特(2的6次方=64)計算其十進制值,根據該值在大小為64的碼表中找到對應的字符,最終得到一個文本字符串。
Base64編碼的作用:
- 由於某些系統中只能使用ASCII字符。Base64用來將非ASCII字符的數據轉換成ASCII字符。
比如我們的電子郵件系統,一般是使用SMTP(簡單郵件傳輸協議)將郵件從客戶端發往服務器端,郵件客戶端使用POP3(郵局協議,第3版本)或IMAP(交互郵件訪問協議)從服務器端獲取郵件。
SMTP協議一開始是基於純ASCII文本的,對於二進制文件(比如郵件附件中的圖像、聲音等)的處理並不好,因為標准ASCII編碼最高位不是數據位,會把二進制文件的最高位作為不可見字符,可能傳輸的過程中當做校驗位處理掉了,從而導致傳輸錯誤。Base64可以將非ASCII字符的數據轉換成ASCII字符。
標准ASCII碼的最高位是奇偶校驗位,比如奇校驗規定:正確的代碼一個字節中1的個數必須是奇數,若非奇數,則在最高位b7添1;偶校驗規定:正確的代碼一個字節中1的個數必須是偶數,若非偶數,則在最高位b7添
-
HTML內嵌Base64編碼圖片
前端在實現頁面時,對於一些簡單圖片,通常會選擇將圖片內容直接內嵌在頁面中,避免不必要的外部資源加載和Http請求,比如Data URIs
,允許使用Base64對圖片或其他文件的二進制數據進行編碼,將其作為文本字符串嵌入網頁中。以百度搜索首頁為例,其中語音搜索的圖標是個背景圖片,其內容以 Data URLs 形式直接寫在css中,這個css內容又直接嵌在HTML頁面中,如下圖所示:
-
很多場景下的數據傳輸要求數據只能由簡單通用的字符組成,比如HTTP協議要求請求的首行和請求頭都必須是ASCII編碼。Base-64編碼將用戶輸入或二進制數據,打包成一種安全格式,將其作為HTTP首部字段的值發送出去,而無須擔心其中包含會破壞HTTP分析程序的冒號、換行符或二進制值。
原理
base64其實不是安全領域下的加密解密算法。雖然有時候經常看到所謂的base64加密解密。其實base64只能算是一個編碼算法,對數據內容進行編碼來適合傳輸。雖然base64編碼過后原文也變成不能看到的字符格式,但也僅此而已。
它的算法是:每3個字節(每字節8bit),轉換為4個6bit的字節(一個字節應該是8bit,所以前2位補0)。例如:
xxxxxxxx yyyyyyyy xxxxyyyy這里轉換前的3個字節,然后,每6位分到一個字節中:
xxxxxx xxyyyy yyyyxx xxyyyy
然后高位補0
00xxxxxx 00xxyyyy 00yyyyxx 00xxyyyy
其中xy是二進制的0和1,然后再按base64碼表進行替換(base64,基本的64個碼,=號不在其內),base64編碼后的字符串只包含字母A-Z,a-z,數字0-9,還有+/這2個特殊字符。
也就是說,轉換后的字符串理論上將要比原來的長1/3。因此Base64所造成數據冗余不是很嚴重,Base64是當今比較流行的編碼方法,因為它編起來速度快而且簡單
舉個例子,有三個字節的原始數據:aaaaaabb bbbbccccc ccdddddd(這里每個字母表示一個bit位)
那么編碼之后會變成: 00aaaaaa 00bbbbbb 00cccccc 00dddddd
所以可以看出base64編碼簡單,雖然編碼后不是明文,看不出原文,但是解碼也很簡單
原文的字節不夠的地方可以用全0來補足,轉換時Base64編碼用=號來代替。
4. 實現
上面的知識點純屬復制粘貼,復習一下知識點,下面開始實現代碼(源碼)。
項目的目錄結構:
采用Spring+SpringMVC+MyBatis+MySQL+Maven+CropBox實現,主要功能:
- 圖片上傳,裁剪
- 預覽
- 后台保存到數據庫和文件夾
- 縮放輸出
下面為從后台數據庫或者文件夾取出的圖片,然后byte[]格式傳到前台, 前一個頭像正常大小,后一個縮放到100*100像素
下面貼出主要代碼:
視圖層
- index.html
實現頭像裁剪,上傳,預覽,
這里利用了jQuery.ajax()函數,不懂得去復習下吧
官網
我的博客里也有簡要介紹:http://www.cnblogs.com/xzwblog/p/6915213.html#_label5
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>CropBox頭像裁剪,上傳,回顯</title>
<link rel="stylesheet" href="/css/style.css" type="text/css" />
</head>
<body>
<script type="text/javascript" src="/js/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="/js/cropbox.js"></script>
<div class="container">
<div class="imageBox">
<div class="thumbBox"></div>
<div class="spinner" style="display: none">Loading...</div>
</div>
<div class="action">
<!-- <input type="file" id="file" style=" width: 200px">-->
<div class="new-contentarea tc"><a href="javascript:void(0)" class="upload-img">
<label for="upload-file">上傳圖像</label>
</a>
<input type="file" class="" name="upload-file" id="upload-file"/>
</div>
<input type="button" id="btnCrop" class="Btnsty_peyton" value="裁切">
<input type="button" id="btnZoomIn" class="Btnsty_peyton" value="+">
<input type="button" id="btnZoomOut" class="Btnsty_peyton" value="-">
<input type="button" id="blobSubmit" class="Btnsty_peyton" value="提交">
</div>
<div class="cropped"></div>
</div>
<script type="text/javascript">
$(window).load(function() {
var options =
{
thumbBox: '.thumbBox',
spinner: '.spinner',
imgSrc: 'images/avatar.png'
}
var cropper = $('.imageBox').cropbox(options);
$('#upload-file').on('change', function(){
var reader = new FileReader();
reader.onload = function(e) {
options.imgSrc = e.target.result;
cropper = $('.imageBox').cropbox(options);
}
reader.readAsDataURL(this.files[0]);
this.files = [];
})
$('#blobSubmit').on('click', function(){
var img = cropper.getBlob();
var formdata = new FormData();
formdata.append("imagefile", img);
$.ajax({
url:"/file/updateHeadPicture.action",
data: formdata,
type:"post",
//默認值: true。默認情況下,通過data選項傳遞進來的數據,如果是一個對象(技術上講只要不是字符串),
// 都會處理轉化成一個查詢字符串,以配合默認內容類型 "application/x-www-form-urlencoded"。如果要發送 DOM 樹信息或其它不希望轉換的信息,請設置為 false。
processData: false,
contentType: false,
success: function(oResult) {
if(oResult.success==1){
window.location.href="/image";
}else{
alert(oResult.message);
}
}
})
})
$('#btnCrop').on('click', function(){
var img = cropper.getDataURL();
$('.cropped').html('');
$('.cropped').append('<img src="'+img+'" align="absmiddle" style="width:64px;margin-top:4px;border-radius:64px;box-shadow:0px 0px 12px #7E7E7E;" ><p>64px*64px</p>');
$('.cropped').append('<img src="'+img+'" align="absmiddle" style="width:128px;margin-top:4px;border-radius:128px;box-shadow:0px 0px 12px #7E7E7E;"><p>128px*128px</p>');
$('.cropped').append('<img src="'+img+'" align="absmiddle" style="width:180px;margin-top:4px;border-radius:180px;box-shadow:0px 0px 12px #7E7E7E;"><p>180px*180px</p>');
})
$('#btnZoomIn').on('click', function(){
cropper.zoomIn();
})
$('#btnZoomOut').on('click', function(){
cropper.zoomOut();
})
});
</script>
</div>
</body>
</html>
- image.html
從后台獲得byte[]格式的字節流,然后展示經過后台處理后的效果:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>頭像展示</title>
</head>
<body>
<div id="forAppend" class="demo"></div>
<img src="/image/xie" alt=""/>
##單位是像素,並且是按照長和寬中較小的值來確定等比例縮放的比例
<img src="/image/xie/300/100" alt=""/>
</body>
</html>
控制層
package com.cropbox.demo.uploadHead.controller;
import com.alibaba.fastjson.JSON;
import com.cropbox.demo.uploadHead.mapper.UserMapper;
import com.cropbox.demo.uploadHead.model.UploadPictureResponse;
import com.cropbox.demo.uploadHead.model.User;
import com.cropbox.demo.uploadHead.service.UploadService;
import com.cropbox.demo.uploadHead.utils.ImageUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
/**
* getBlob:上傳圖片為Blob格式,並保存到mysql的blob字段中,
* 其實File繼承了Blob,所以表單中的圖片處理方式與之類似,
* 實現頭像的裁剪,保存到服務器,並在需要時回顯到客戶端
*
* @author xie
* @version 1.0
* @Date 2017/5/26
*/
@Controller
public class FileController {
@Autowired
UploadService uploadService;
@Autowired
UserMapper userMapper;
@Autowired
ImageUtils imageUtils;
/**
* 主頁
* @return
*/
@RequestMapping(path = {"/"}, method = {RequestMethod.GET, RequestMethod.POST})
public String index() {
return "index";
}
/**
* 實現圖片上傳
* @param file
* @param response
*/
@RequestMapping(path = {"/file/updateHeadPicture.action"}, method = {RequestMethod.GET, RequestMethod.POST})
public void index(@RequestParam("imagefile") MultipartFile file, HttpServletResponse response) {
try {
UploadPictureResponse uploadPictureResponse = uploadService.updateHeadPicture(file);
/*
設置編碼格式,返回結果json結果,注意其中的對象轉化為json字符串格式為:
{"message":"上傳圖片成功!","success":1,"url":"C:\\\\home\\\\myblog\\\\pic\\\\2f1b63bc4b654a27a7e0c1b1a0fb9270.png"}
所以前端可以直接讀取success,message等信息
*/
response.setContentType( "application/json;charset=UTF-8");
response.getWriter().write( JSON.toJSONString(uploadPictureResponse));
} catch (IOException e1) {
e1.printStackTrace();
}
}
@RequestMapping(path= {"/image"}, method = {RequestMethod.GET, RequestMethod.POST})
public String index1() {
return "image";
}
/**
* 按照用戶名查找頭像
* @param username
* @param response
*/
@RequestMapping(path = {"/image/{username}"}, method = {RequestMethod.GET, RequestMethod.POST})
public void index1(@PathVariable("username") String username, HttpServletResponse response) {
User user = userMapper.selectByUsername(username);
try {
//寫到輸出流
response.setContentType("image/png");
response.setCharacterEncoding("UTF-8");
//BufferedOutputStream 是緩沖輸出流,默認新建字節數組大小為8192的“緩沖輸出流”
OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
outputStream.write(user.getHead());
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 按照用戶名查找頭像,並提供縮放功能
* @param username 用戶名
* @param width 要求圖片的寬度
* @param height 要求圖片的高度
* @param response
*/
@RequestMapping(path = "/image/{username}/{width}/{height}")
public void getPhotoById(@PathVariable("username") String username, @PathVariable("width") int width,
@PathVariable("height") int height, HttpServletResponse response) {
User user = userMapper.selectByUsername(username);
byte[] data = user.getHead();
try {
if (width > 0 && height > 0) {
data = imageUtils.scaleImage(data, width, height);
}
response.setContentType("image/png");
response.setCharacterEncoding("UTF-8");
OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
outputStream.write(data);
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
service層
package com.cropbox.demo.uploadHead.service;
import com.cropbox.demo.uploadHead.mapper.UserMapper;
import com.cropbox.demo.uploadHead.model.UploadPictureResponse;
import com.cropbox.demo.uploadHead.utils.ImageUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.UUID;
/**
* 類的詳細說明
*
* @author xie
* @version 1.0
* @Date 2017/5/27
*/
@Service
public class UploadService {
@Autowired
ImageUtils imageUtils;
@Autowired
UserMapper userMapper;
/**
* 上傳的頭像統一都轉換為了png格式,故不進行是否允許類型判斷
* @param file
* @return
* @throws IOException
*/
public UploadPictureResponse updateHeadPicture(MultipartFile file) throws IOException {
UploadPictureResponse uploadPictureResponse = new UploadPictureResponse();
try {
InputStream is = file.getInputStream();
byte[] bytes = FileCopyUtils.copyToByteArray(is);
//更新數據庫中的blob格式的head字段,返回1表示更新成功,返回0表示失敗
int success = userMapper.updateHead(1,bytes);
//上面已經將輸入流中的數據全部讀完,故重新初始化
is = file.getInputStream();
//同時將圖片保存到C:\\home\\myblog\\pic\\ 路徑下,這里保存到文件夾只是演示作用,請根據需求決定將圖片保存到數據庫還是服務器文件夾
String fileName = UUID.randomUUID().toString().replaceAll("-", "") + ".png" ;
Files.copy(is, new File(imageUtils.getPictureDir() + fileName).toPath(),
StandardCopyOption.REPLACE_EXISTING);
uploadPictureResponse.setSuccess(success);
uploadPictureResponse.setMessage("上傳圖片成功!");
uploadPictureResponse.setUrl(imageUtils.getPictureDir() + fileName);
is.close();
return uploadPictureResponse;
} catch (Exception e) {
// 請求失敗時打印的異常的信息
uploadPictureResponse.setSuccess(0);
uploadPictureResponse.setMessage("服務器異常!");
return uploadPictureResponse;
}
}
}
圖片處理工具類
package com.cropbox.demo.uploadHead.utils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
* 圖片處理服務
*
* @author xie
* @version 1.0
* @Date 2017/5/27
*/
@Service
public class ImageUtils {
/** 頭像圖片的放置路徑*/
@Value("${headPath.home}")
private String PictureDir;
/** 允許的圖片類型頭像圖片,這里分別使用屬性占位符和SpEL表達式,可以實現更復雜的功能,運行時計算值*/
@Value("${pictureLimit.suffix}")
private String PictureFileSuffix;
/**
判斷上傳圖片格式是否被允許
*/
public boolean isFileAllowed(String fileSuffix) {
for (String suffix : PictureFileSuffix.split(",")) {
if (suffix.equals(fileSuffix)) {
return true;
}
}
return false;
}
/**
* 獲得圖片存儲路徑
* @return
*/
public String getPictureDir(){
return PictureDir;
}
/**
* 獲得系統允許上傳圖片后綴
* @return
*/
public String getPictureFileSuffix(){
return PictureFileSuffix;
}
/**
* 等比例縮放圖片,按照長和寬中較小的數來確定縮放比例,所有單位為像素,
* 在傳輸中,圖片是不能直接傳的,因此需要把圖片變為字節數組,然后傳輸比較方便;只需要一般輸出流的write方法即可;而字節數組變成BufferedImage能夠還原圖像;
*
* @param data 圖片的byte[]格式
* @param width 縮放后的寬度
* @param height 縮放后的高度
* @return 圖片縮放后的byte[]格式
* @throws IOException
*/
public byte[] scaleImage(byte[] data, int width, int height) throws IOException {
////從特定文件載入
BufferedImage oldImage = ImageIO.read(new ByteArrayInputStream(data));
int imageOldWidth = oldImage.getWidth();
int imageOldHeight = oldImage.getHeight();
double scale_x = (double) width / imageOldWidth;
double scale_y = (double) height / imageOldHeight;
double scale_xy = Math.min(scale_x, scale_y);
int imageNewWidth = (int) (imageOldWidth * scale_xy);
int imageNewHeight = (int) (imageOldHeight * scale_xy);
//創建一個不帶透明色的BufferedImage對象
BufferedImage newImage = new BufferedImage(imageNewWidth, imageNewHeight, BufferedImage.TYPE_INT_RGB);
/*BufferedImage與Image之間的相互轉換,其中
* oldImage.getScaledInstance(imageNewWidth, imageNewHeight, BufferedImage.SCALE_SMOOTH)表示縮放圖像
* BufferedImage.SCALE_SMOOTH表示壓縮圖片所用的算法,本算法生成縮略圖片的平滑度的優先級比速度高,生成的圖片質量比較好,但速度慢
*
*/
newImage.getGraphics().drawImage(oldImage.getScaledInstance(imageNewWidth, imageNewHeight, BufferedImage.SCALE_SMOOTH), 0, 0, null);
/*
釋放繪圖上下文所占的系統資源
*/
newImage.getGraphics().dispose();
ByteArrayOutputStream outPutStream = new ByteArrayOutputStream();
/*BufferedImage ---->byte[],
參數newImage表示獲得的BufferedImage;
參數format表示圖片的格式,比如“gif”等;
參數out表示輸出流,如果要轉成Byte數組,則輸出流為ByteArrayOutputStream即可;
執行完后,只需要toByteArray()就能得到byte[];
*/
ImageIO.write(newImage, "jpg", outPutStream);
oldImage.flush();
outPutStream.flush();
outPutStream.close();
return outPutStream.toByteArray();
}
}
分析
利用Fiddler我們簡要分析一下http的請求與回應。
如下圖所示,當我們點擊了提交
按鈕
http各字段的含義:http://www.cnblogs.com/xzwblog/p/6917960.html
其中:
Content-Length: 78174
表示傳輸的二進制圖片的大小,單位字節
Content-Disposition: form-data; name="imagefile"; filename="blob"
作為對下載文件的一個標識字段。
在請求時,form-data
表示上傳表單數據,name="imagefile"
表示表單參數的名字, filename="blob"
表示文件名
在回應時,Content-Disposition有兩種屬性,inline 和 attachment。 inline :將文件內容直接顯示在頁面, attachment:彈出對話框讓用戶下載。
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryrmcDIMhUIGDLILq1
表示利用表單提交數據,boundary是為了區分POST的內容的區段用的,只要在內容中遇到了此值,就表示下面要開始一個新的區段了,每個區段的內容相對獨立。如果遇到的是此值后面連着兩個減號,則表示全部內容到此結束。每個段也分為段頭和段體兩部分,用空行隔開,每段都有自己的類型和相關信息。比如下面的請求,第一區段是text1的值,它的名稱是“text1”,值為“hehe”。第二段是文件內容,段首里表明了此文件域的名稱“file1”和此文件在用戶磁盤上的位置,后面就是文件的內容。
POST /form.asp HTTP/1.1
Accept: */*
Referer: http://localhost:8080/form.asp
Accept-Language: zh-cn
Content-Type: multipart/form-data; boundary=---------------------------7d62bf2f9066c
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; InfoPath.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)
Host: localhost:8080
Content-Length: 337
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: key=haha; ASPSESSIONIDCSQCRTBS=LOIPGIMBLMNOGCOBOMPJBOKP
-----------------------------7d62bf2f9066c
Content-Disposition: form-data; name="text1"
hehe
-----------------------------7d62bf2f9066c
Content-Disposition: form-data; name="file1"; filename="H:\Documents and Settings\Administrator\桌面\haha.txt"
Content-Type: text/plain
ABCDEFG
-----------------------------7d62bf2f9066c--
{"message":"上傳圖片成功!","success":1,"url":"C:\\\\home\\\\myblog\\\\pic\\\\3973974dd2bb41b49fe064dedc0dcae9.png"}
表示回應的json字符串,即項目中的JSON.toJSONString(uploadPictureResponse)部分,將uploadPictureResponse實體對象轉化為json字符串。