如何用CropBox實現頭像裁剪並與java后台交互


如何用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 圖像裁剪插件有JcropCropBox,前者是將原圖和需要裁剪的參數(裁剪的各點坐標,旋轉角度等)傳到后台,然后由后台完成實際的裁剪和后續操作。
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

用處:

  1. 大文件分割 (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像素

    下面貼出主要代碼:

視圖層

cropbox用法:
github上的說明和Demo

<!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字符串。

源碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM