[Bootstrap-插件使用]Jcrop+fileinput組合實現頭像上傳功能

好久沒有更新博客了,再不寫點東西都爛了。css

此次更新一個小內容,是兩個插件的組合使用,實現頭像上傳功能。html

業務需求:


  • 頭像上傳功能,要對上傳的文件進行剪切,且保證頭像到服務器時必須是正方形的。
  • 優化<input type="file">的顯示樣式,基礎的樣式實在太難看了。
  • 上傳的頭像須要進行質量壓縮跟大小裁剪,以減緩瀏覽器的壓力。

成果預覽:前端

 

使用到的技術插件


  • Jcrop:用於前端「裁剪」圖片java

  • bootstrap-fileinput:用於前端優化上傳控件樣式
  • ARTtemplate:JS版的JSTL?反正就是一個騰訊的模板化插件,很好用,真心。
  • bootstrap-sco.modal.js:這個是bootstrap的一個模態插件git

  • SpringMVC:使用框架自帶的MultipartFile來獲取文件(效率可以大大的提升)github

  • Image:這個是Java的內置類,用於處理圖片很方便。

原理說明


 

  首先是Jcrop這個前端JS插件,這個插件很好用,其實在各大網站中也很常見,先上圖:web

沒錯,這個一臉懵逼的就是我。。。ajax

  說說原理,實際上,Jcrop並無在客戶端幫咱們把圖片進行裁剪,只是收集了用戶的「裁剪信息」,而後傳到後端,最後的裁剪和壓縮,仍是要依靠服務器上的代碼來進行。算法

  咱們能夠看到這個插件在圖片上顯示出了8個控制點,讓用戶選擇裁剪區域,當用戶選擇成功後,會自動的返回「裁剪信息」,所謂的裁剪信息,其實就是選框的左上角原點座標,及裁剪框的寬度和高度,經過這四個值,在後端就能夠進行裁剪工做了。bootstrap

  可是咱們要注意,用戶在上傳圖片的時候,長度寬度都是不規則的,固然咱們能夠用bootstap-fileinput這個插件去限制用戶只能上傳指定寬高的圖片,但這就失去了咱們「裁剪」的意義,並且用戶的體驗就很是差勁。然而jcrop所返回的座標值及寬高,並非基於所上傳圖片自身的像素,而是如圖中所示,是外層DIV的寬高。舉一個例子,上圖我實際放入的我的照片寬度是852px,可是Jcrop的截取寬度是312px,這個312px並非真正圖片上的實際寬度,是通過縮放後的寬度,因此咱們後端必定須要從新對這個312px進行一次還原,還原到照片實際比例的寬度。

  好啦,原理就是這樣子。接下來,就是上代碼了。

HTML


 

 1 <script id="portraitUpload" type="text/html">
 2     <div style="padding: 10px 20px">
 3         <form role="form" enctype="multipart/form-data" method="post">
 4             <div class="embed-responsive embed-responsive-16by9">
 5                 <div class="embed-responsive-item pre-scrollable">
 6                     <img alt="" src="${pageContext.request.contextPath}/img/showings.jpg" id="cut-img"
 7                          class="img-responsive img-thumbnail"/>
 8                 </div>
 9             </div>
10             <div class="white-divider-md"></div>
11             <input type="file" name="imgFile" id="fileUpload"/>
12             <div class="white-divider-md"></div>
13             <div id="alert" class="alert alert-danger hidden" role="alert"></div>
14             <input type="hidden" id="x" name="x"/>
15             <input type="hidden" id="y" name="y"/>
16             <input type="hidden" id="w" name="w"/>
17             <input type="hidden" id="h" name="h"/>
18         </form>
19     </div>
20 </script>

  這個就是一個ArtTemplate的模板代碼,就寫在</body>標籤上方就好了,由於text/html這個類型,不會被識別,因此實際上用Chrome調試就能夠看獲得,前端用戶是看不到這段代碼的。

  簡單解釋一下這個模板,這個模板是我最後放入模態窗口時用的模板,就是把這段代碼,直接丟進模態彈出來的內容部分。由於是文件上傳,天然須要套一個<form>標籤,而後必須給form標籤放入 enctype="multipart/form-data",不然後端Spring就沒法獲取這個文件。

  "embed-responsive embed-responsive-16by9"這個類就是用來限制待編輯圖片加載後的寬度大小,值得注意的是,我在其內種,加了一個

<div class="embed-responsive-item pre-scrollable">

  pre-scrollable這個類,會讓加載的圖片不會由於太大而「變形」,由於我外層經過embed-responsive-16by9限制死了圖片的寬高,圖片自己又加了img-responsive這個添加響應式屬性的類,爲了防止圖片縮放,致使截圖障礙,因此就給內層加上pre-scrollable,這個會給圖片這一層div加上滾動條,若是圖片高度過高,超過了外層框,則會出現滾動條,而這不會影響圖片截取,同時又保證了模態窗口不會「太長」,致使體驗糟糕(尤爲在移動端)。

  底下四個隱藏域相信你們看他們的name值也就知道個大概,這個就是用於存放Jcrop截取時所產生的原點座標和截取寬高的值。

JS


 

 1 $(document).ready(function () {
 2     new PageInit().init();
 3 });
 4 
 5 
 6 function PageInit() {
 7     var api = null;
 8     var _this = this;
 9     this.init = function () {
10                $("[name='upload']").on('click', this.portraitUpload);
11     };
12 
13     this.portraitUpload = function () {
14         var model = $.scojs_modal({
15                 title: '頭像上傳',
16                 content: template('portraitUpload'),
17                 onClose: refresh
18             }
19         );
20         model.show();
21         var fileUp = new FileUpload();
22         var portrait = $('#fileUpload');
23         fileUp.portrait(portrait, '/upload/portrait', _this.getExtraData, $('#alert'));
24         portrait.on('change', _this.readURL);
25     };
26 
27     this.readURL = function () {
28         var img = $('#cut-img');
29         var input = $('#fileUpload');
30         if (input[0].files && input[0].files[0]) {
31             var reader = new FileReader();
32             reader.readAsDataURL(input[0].files[0]);
33             reader.onload = function (e) {
34                 img.removeAttr('src');
35                 img.attr('src', e.target.result);
36                 img.Jcrop({
37                     setSelect: [20, 20, 200, 200],
38                     handleSize: 10,
39                     aspectRatio: 1,
40                     onSelect: updateCords
41                 }, function () {
42                     api = this;
43                 });
44             };
45             if (api != undefined) {
46                 api.destroy();
47             }
48         }
49         function updateCords(obj) {
50             $("#x").val(obj.x);
51             $("#y").val(obj.y);
52             $("#w").val(obj.w);
53             $("#h").val(obj.h);
54         }
55     };
56 
57     this.getExtraData = function () {
58         return {
59             sw: $('.jcrop-holder').css('width'),
60             sh: $('.jcrop-holder').css('height'),
61             x: $('#x').val(),
62             y: $('#y').val(),
63             w: $('#w').val(),
64             h: $('#h').val()
65         }
66     }
67 }

  這個JS是上傳頁面的相關邏輯。會JS的人都看得懂它的意義,我就簡單說一下幾個事件的意義:

1  portrait.on('fileuploaderror', function (event, data, msg) {
2             alert.removeClass('hidden').html(msg);
3             fileUp.fileinput('disable');
4         });

  這個事件,是用於bootstrap-fileinput插件在校驗文件格式、文件大小等的時候,若是不符合咱們的要求,則會對前面HTML代碼中有一個

<div id="alert" class="alert alert-danger hidden" role="alert"></div>

  進行一些錯誤信息的顯示操做。

1 portrait.on('fileclear', function (event) {
2             alert.addClass('hidden').html();
3         });

  這部分代碼,是當文件移除時,隱藏錯誤信息提示區,以及清空內容,固然這是符合咱們的業務邏輯的。

1  portrait.on('fileloaded', function (event, file, previewId, index, reader) {
2             alert.addClass('hidden').html();
3         });

  這部分代碼是當選擇文件時(此時還沒進行文件校驗),隱藏錯誤信息,清空錯誤內容,這麼作是爲了應對若是上一次文件校驗時有錯誤,而從新選擇文件時,確定要清空上一次的錯誤信息,再顯示本次的錯誤信息。

1 portrait.on('fileuploaded', function (event, data) {
2             if (!data.response.status) {
3                 alert.html(data.response.message).removeClass('hidden');
4             }
5         })

  這部分是當文件上傳後,後端若是返回了錯誤信息,則須要進行相關的提示信息處理。

 1  this.getExtraData = function () {
 2         return {
 3             sw: $('.jcrop-holder').css('width'),
 4             sh: $('.jcrop-holder').css('height'),
 5             x: $('#x').val(),
 6             y: $('#y').val(),
 7             w: $('#w').val(),
 8             h: $('#h').val()
 9         }
10     }

  這部分代碼是獲取上傳文件時,附帶須要發日後端的參數,這裏面能夠看到,x、y天然是Jcrop截取時,選框的左上角原點座標,w、h天然就是截取的寬高,可是剛纔我說了,這個是通過縮放後的寬高,不是依據圖片實際像素的寬高。而sw、sh表明的是scaleWidth、scaleHeight,就是縮放寬高的意思。這個.jcrop-holder的對象是當Jcrop插件啓用後,加載的圖片外層容器的對象,只須要獲取這個對象的寬高,就是圖片被壓縮的寬高,可是由於我限制了圖片的寬度和高度,寬度的比例是定死的(不是寬高定死,只是比例定死,bootstrap自己就是響應式框架,因此不能單純的說寬高定死,寬高會隨着使用終端的變化而變化),高度是根據寬度保持16:4,但是我又加了pre-scrollable這個類讓圖片太高時以滾動條的方式不破壞外層容器的高度,因此咱們實際能拿來計算縮放比例的,是寬度,而不是高度,可是這裏我一塊兒傳,萬一之後有其餘的使用場景,要以高度爲準也說不定。

  好了,而後我須要貼上bootstrap-fileinput插件的配置代碼:

 1 this.portrait = function (target, uploadUrl, data, alert) {
 2         target.fileinput({
 3             language: 'zh', //設置語言
 4             maxFileSize: 2048,//文件最大容量
 5             uploadExtraData: data,//上傳時除了文件之外的其餘額外數據
 6             showPreview: false,//隱藏預覽
 7             uploadAsync: true,//ajax同步
 8             dropZoneEnabled: false,//是否顯示拖拽區域
 9             uploadUrl: uploadUrl, //上傳的地址
10             elErrorContainer: alert,//錯誤信息內容容器
11             allowedFileExtensions: ['jpg'],//接收的文件後綴
12             showUpload: true, //是否顯示上傳按鈕
13             showCaption: true,//是否顯示標題
14             browseClass: "btn btn-primary", //按鈕樣式
15             previewFileIcon: "<i class='glyphicon glyphicon-king'></i>",
16             ajaxSettings: {//這個是由於我使用了SpringSecurity框架,有csrf跨域提交防護,所需須要設置這個值
17                 beforeSend: function (xhr) {
18                     xhr.setRequestHeader(header, token);
19                 }
20             }
21         });
22         this.alert(target, alert);
23     };
24     this.alert = function (target, alert) {
25         target.on('fileuploaderror', function (event, data, msg) {
26             alert.removeClass('hidden').html(msg);
27             _this.fileinput('disable');
28         });
29         target.on('fileclear', function (event) {
30             alert.addClass('hidden').html();
31         });
32         target.on('fileloaded', function (event, file, previewId, index, reader) {
33             alert.addClass('hidden').html();
34         });
35         target.on('fileuploaded', function (event, data) {
36             if (!data.response.status) {
37                 alert.html(data.response.message).removeClass('hidden');
38             }
39         });
40     };

  這個代碼有寫了註釋,我就很少解釋了。

  惟一作一個補充,就是「elErrorContainer: alert,//錯誤信息內容容器」這個配置,這個是我後來再次研究這個插件獲得的一個心得,這個插件自帶錯誤信息顯示的功能,可是吧,至少我還不知道如何可以讓ajax後自定義的錯誤信息調用這個顯示功能,因而我就只能本身定義一個alert的容器,用來存放錯誤信息來擴展這個插件,可是這樣就會在某種狀況下好比400錯誤時,致使出現兩個錯誤信息提示,那解決的辦法我是看到了這個參數,只須要將這個錯誤信息的容器從默認值修改成我自定義的容器就能夠了。

  關於Ajax同步,是由於我我的認爲,上傳文件這個仍是作成同步比較好,等文件上傳完成後,js代碼才能繼續執行下去。由於文件上傳畢竟是一個耗時的工做,有的邏輯又確實須要當文件上傳成功之後才執行,好比刷新頁面,因此爲了不出現問題,仍是作成同步的比較好。還有就是去掉預覽,用過bootstrap-fileinput插件的都知道,這個插件的圖片預覽功能很強大,甚至能夠單獨依靠這個插件來製做相冊管理。可是由於咱們此次要結合Jcrop,因此要割掉這部分功能。

SpringMVC-Controller獲取文件


 

 1 @ResponseBody
 2     @RequestMapping(value = "/portrait", method = {RequestMethod.POST})
 3     public JsonResult upload(HttpServletRequest request) throws Exception {
 4         Integer x = Integer.parseInt(MyStringTools.checkParameter(request.getParameter("x"), "圖片截取異常:X!"));
 5         Integer y = Integer.parseInt(MyStringTools.checkParameter(request.getParameter("y"), "圖片截取異常:Y!"));
 6         Integer w = Integer.parseInt(MyStringTools.checkParameter(request.getParameter("w"), "圖片截取異常:W!"));
 7         Integer h = Integer.parseInt(MyStringTools.checkParameter(request.getParameter("h"), "圖片截取異常:H!"));
 8         String scaleWidthString = MyStringTools.checkParameter(request.getParameter("sw"), "圖片截取異常:SW!");
 9         int swIndex = scaleWidthString.indexOf("px");
10         Integer sw = Integer.parseInt(scaleWidthString.substring(0, swIndex));
11         String scaleHeightString = MyStringTools.checkParameter(request.getParameter("sh"), "圖片截取異常:SH!");
12         int shIndex = scaleHeightString.indexOf("px");
13         Integer sh = Integer.parseInt(scaleHeightString.substring(0, shIndex));
14 
15 
16         //獲取用戶ID用於指向對應文件夾
17         SysUsers sysUsers = HttpTools.getSessionUser(request);
18         int userID = sysUsers.getUserId();
19         //獲取文件路徑
20         String filePath = FileTools.getPortraitPath(userID);
21 
22         CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(
23                 request.getSession().getServletContext());
24 
25         String path;
26         //檢查form中是否有enctype="multipart/form-data"
27         if (multipartResolver.isMultipart(request)) {
28             //將request變成多部分request
29             MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
30             //獲取multiRequest 中全部的文件名
31             Iterator iterator = multiRequest.getFileNames();
32             while (iterator.hasNext()) {
33                 //一次遍歷全部文件
34                 MultipartFile multipartFile = multiRequest.getFile(iterator.next().toString());
35                 if (multipartFile != null) {
36                     String[] allowSuffix = {".jpg",".JPG"};
37                     if (!FileTools.checkSuffix(multipartFile.getOriginalFilename(), allowSuffix)) {
38                         throw new BusinessException("文件後綴名不符合要求!");
39                     }
40                     path = filePath + FileTools.getPortraitFileName(multipartFile.getOriginalFilename());
41                     //存入硬盤
42                     multipartFile.transferTo(new File(path));
43                     //圖片截取
44                     if (FileTools.imgCut(path, x, y, w, h, sw, sh)) {
45                         CompressTools compressTools = new CompressTools();
46                         if (compressTools.simpleCompress(new File(path))) {
47                             return JsonResult.success(FileTools.filePathToSRC(path, FileTools.IMG));
48                         } else {
49                             return JsonResult.error("圖片壓縮失敗!請從新上傳!");
50                         }
51                     } else {
52                         return JsonResult.error("圖片截取失敗!請從新上傳!");
53                     }
54                 }
55             }
56         }
57         return JsonResult.error("圖片獲取失敗!請從新上傳!");
58     }

Image圖片切割


 

 1 /**
 2      * 截圖工具,根據截取的比例進行縮放裁剪
 3      *
 4      * @param path        圖片路徑
 5      * @param zoomX       縮放後的X座標
 6      * @param zoomY       縮放後的Y座標
 7      * @param zoomW       縮放後的截取寬度
 8      * @param zoomH       縮放後的截取高度
 9      * @param scaleWidth  縮放後圖片的寬度
10      * @param scaleHeight 縮放後的圖片高度
11      * @return 是否成功
12      * @throws Exception 任何異常均拋出
13      */
14     public static boolean imgCut(String path, int zoomX, int zoomY, int zoomW,
15                                  int zoomH, int scaleWidth, int scaleHeight) throws Exception {
16         Image img;
17         ImageFilter cropFilter;
18         BufferedImage bi = ImageIO.read(new File(path));
19         int fileWidth = bi.getWidth();
20         int fileHeight = bi.getHeight();
21         double scale = (double) fileWidth / (double) scaleWidth;
22 
23         double realX = zoomX * scale;
24         double realY = zoomY * scale;
25         double realW = zoomW * scale;
26         double realH = zoomH * scale;
27 
28         if (fileWidth >= realW && fileHeight >= realH) {
29             Image image = bi.getScaledInstance(fileWidth, fileHeight, Image.SCALE_DEFAULT);
30             cropFilter = new CropImageFilter((int) realX, (int) realY, (int) realW, (int) realH);
31             img = Toolkit.getDefaultToolkit().createImage(
32                     new FilteredImageSource(image.getSource(), cropFilter));
33             BufferedImage bufferedImage = new BufferedImage((int) realW, (int) realH, BufferedImage.TYPE_INT_RGB);
34             Graphics g = bufferedImage.getGraphics();
35             g.drawImage(img, 0, 0, null);
36             g.dispose();
37             //輸出文件
38             return ImageIO.write(bufferedImage, "JPEG", new File(path));
39         } else {
40             return true;
41         }
42     }

  縮放比例scale必定要用double,而且寬高也要轉換成double後再相除,不然會變成求模運算,這樣會下降精度,別小看這裏的精度降低,最終的截圖效果根據圖片的縮放程度,偏差但是有可能被放大的很離譜的。

 

圖片壓縮


  1 package com.magic.rent.tools;
  2 
  3 /**
  4  * 知識產權聲明:本文件自建立起,其內容的知識產權即歸屬於原做者,任何他人不可擅自複製或模仿.
  5  * 建立者: wu   建立時間: 2016/12/15
  6  * 類說明: 縮略圖類(通用) 本java類能將jpg、bmp、png、gif圖片文件,進行等比或非等比的大小轉換。 具體使用方法
  7  * 更新記錄:
  8  */
  9 
 10 import com.magic.rent.exception.custom.BusinessException;
 11 import com.sun.image.codec.jpeg.JPEGCodec;
 12 import com.sun.image.codec.jpeg.JPEGImageEncoder;
 13 import org.slf4j.Logger;
 14 import org.slf4j.LoggerFactory;
 15 
 16 import javax.imageio.ImageIO;
 17 import java.awt.*;
 18 import java.awt.image.BufferedImage;
 19 import java.io.File;
 20 import java.io.FileOutputStream;
 21 
 22 public class CompressTools {
 23     private File file; // 文件對象
 24     private String inputDir; // 輸入圖路徑
 25     private String outputDir; // 輸出圖路徑
 26     private String inputFileName; // 輸入圖文件名
 27     private String outputFileName; // 輸出圖文件名
 28     private int outputWidth = 100; // 默認輸出圖片寬
 29     private int outputHeight = 100; // 默認輸出圖片高
 30     private boolean proportion = true; // 是否等比縮放標記(默認爲等比縮放)
 31     private static Logger logger = LoggerFactory.getLogger(CompressTools.class);
 32 
 33 
 34     public CompressTools() {
 35     }
 36 
 37     public CompressTools(boolean proportion) {
 38         this.proportion = proportion;
 39     }
 40 
 41     /**
 42      * 設置輸入參數
 43      *
 44      * @param inputDir
 45      * @param inputFileName
 46      * @return
 47      */
 48     public CompressTools setInputInfo(String inputDir, String inputFileName) {
 49         this.inputDir = inputDir;
 50         this.inputFileName = inputFileName;
 51         return this;
 52     }
 53 
 54     /**
 55      * 設置輸出參數
 56      *
 57      * @param outputDir
 58      * @param outputFileName
 59      * @param outputHeight
 60      * @param outputWidth
 61      * @param proportion
 62      * @return
 63      */
 64     public CompressTools setOutputInfo(String outputDir, String outputFileName, int outputHeight, int outputWidth, boolean proportion) {
 65         this.outputDir = outputDir;
 66         this.outputFileName = outputFileName;
 67         this.outputWidth = outputWidth;
 68         this.outputHeight = outputHeight;
 69         this.proportion = proportion;
 70         return this;
 71     }
 72 
 73 
 74     // 圖片處理
 75     public boolean compress() throws Exception {
 76         //得到源文件
 77         file = new File(inputDir);
 78         if (!file.exists()) {
 79             throw new BusinessException("文件不存在!");
 80         }
 81         Image img = ImageIO.read(file);
 82         // 判斷圖片格式是否正確
 83         if (img.getWidth(null) == -1) {
 84             System.out.println(" can't read,retry!" + "<BR>");
 85             return false;
 86         } else {
 87             int newWidth;
 88             int newHeight;
 89             // 判斷是不是等比縮放
 90             if (this.proportion) {
 91                 // 爲等比縮放計算輸出的圖片寬度及高度
 92                 double rate1 = ((double) img.getWidth(null)) / (double) outputWidth + 0.1;
 93                 double rate2 = ((double) img.getHeight(null)) / (double) outputHeight + 0.1;
 94                 // 根據縮放比率大的進行縮放控制
 95                 double rate = rate1 > rate2 ? rate1 : rate2;
 96                 newWidth = (int) (((double) img.getWidth(null)) / rate);
 97                 newHeight = (int) (((double) img.getHeight(null)) / rate);
 98             } else {
 99                 newWidth = outputWidth; // 輸出的圖片寬度
100                 newHeight = outputHeight; // 輸出的圖片高度
101             }
102             long start = System.currentTimeMillis();
103             BufferedImage tag = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
104             /*
105              * Image.SCALE_SMOOTH 的縮略算法 生成縮略圖片的平滑度的
106              * 優先級比速度高 生成的圖片質量比較好 但速度慢
107              */
108             tag.getGraphics().drawImage(img.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH), 0, 0, null);
109             FileOutputStream out = new FileOutputStream(outputDir);
110 
111             // JPEGImageEncoder可適用於其餘圖片類型的轉換
112             JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
113             encoder.encode(tag);
114             out.close();
115             long time = System.currentTimeMillis() - start;
116             logger.info("[輸出路徑]:" + outputDir + "\t[圖片名稱]:" + outputFileName + "\t[壓縮前大小]:" + getPicSize() + "\t[耗時]:" + time + "毫秒");
117             return true;
118         }
119     }
120 
121 
122     /**
123      * 簡單壓縮方法,壓縮後圖片將直接覆蓋源文件
124      *
125      * @param images
126      * @return
127      * @throws Exception
128      */
129     public boolean simpleCompress(File images) throws Exception {
130         setInputInfo(images.getPath(), images.getName());
131         setOutputInfo(images.getPath(), images.getName(), 300, 300, true);
132         return compress();
133     }
134 
135     /**
136      * 獲取圖片大小,單位KB
137      *
138      * @return
139      */
140     private String getPicSize() {
141         return file.length() / 1024 + "KB";
142     }
143 
144     public static void main(String[] args) throws Exception {
145         CompressTools compressTools = new CompressTools();
146         compressTools.setInputInfo("/Users/wu/Downloads/background.jpg", "background.jpg");
147         compressTools.setOutputInfo("/Users/wu/Downloads/background2.jpg", "background2.jpg", 633, 1920, false);
148         compressTools.compress();
149     }
150 
151 }

  我專門把圖片壓縮寫成了一個類。

  其中能夠看到一些關於文件路徑的方法,其實沒有什麼特別的,就是截取後綴獲取路徑之類的,我這邊也貼出來吧,省得有些朋友看的雲裏霧裏的。

  1 package com.magic.rent.tools;
  2 
  3 import com.magic.rent.exception.custom.BusinessException;
  4 
  5 import javax.imageio.ImageIO;
  6 import java.awt.*;
  7 import java.awt.image.BufferedImage;
  8 import java.awt.image.CropImageFilter;
  9 import java.awt.image.FilteredImageSource;
 10 import java.awt.image.ImageFilter;
 11 import java.io.File;
 12 import java.util.ArrayList;
 13 
 14 /**
 15  * 知識產權聲明:本文件自建立起,其內容的知識產權即歸屬於原做者,任何他人不可擅自複製或模仿.
 16  * 建立者: wu   建立時間: 2016/11/25
 17  * 類說明:
 18  * 更新記錄:
 19  */
 20 public class FileTools {
 21 
 22     public static final int IMG = 1;
 23 
 24     /**
 25      * 獲取項目根目錄
 26      *
 27      * @return 根目錄
 28      */
 29     public static String getWebRootPath() {
 30         return System.getProperty("web.root");
 31     }
 32 
 33     /**
 34      * 獲取頭像目錄,若不存在則直接建立一個
 35      *
 36      * @param userID 用戶ID
 37      * @return
 38      */
 39     public static String getPortraitPath(int userID) {
 40         String realPath = getWebRootPath() + "img/portrait/" + userID + "/";
 41         File file = new File(realPath);
 42         //判斷文件夾是否存在,不存在則建立一個
 43         if (!file.exists() || !file.isDirectory()) {
 44             if (!file.mkdirs()) {
 45                 throw new BusinessException("建立頭像文件夾失敗!");
 46             }
 47         }
 48         return realPath;
 49     }
 50 
 51     /**
 52      * 重命名頭像文件
 53      *
 54      * @param fileName 文件名
 55      * @return
 56      */
 57     public static String getPortraitFileName(String fileName) {
 58         // 獲取文件後綴
 59         String suffix = getSuffix(fileName);
 60         return "portrait" + suffix;
 61     }
 62 
 63     /**
 64      * 判斷文件後綴是否符合要求
 65      *
 66      * @param fileName    文件名
 67      * @param allowSuffix 容許的後綴集合
 68      * @return
 69      * @throws Exception
 70      */
 71     public static boolean checkSuffix(String fileName, String[] allowSuffix) throws Exception {
 72         String fileExtension = getSuffix(fileName);
 73         boolean flag = false;
 74         for (String extension : allowSuffix) {
 75             if (fileExtension.equals(extension)) {
 76                 flag = true;
 77             }
 78         }
 79         return flag;
 80     }
 81 
 82 
 83     public static String getSuffix(String fileName) {
 84         return fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
 85     }
 86 
 87     /**
 88      * 將文件地址轉成連接地址
 89      *
 90      * @param filePath 文件路徑
 91      * @param fileType 文件類型
 92      * @return
 93      */
 94     public static String filePathToSRC(String filePath, int fileType) {
 95         String href = "";
 96         if (null != filePath && !filePath.equals("")) {
 97             switch (fileType) {
 98                 case IMG:
 99                     if (filePath.contains("/img/")) {
100                         int index = filePath.indexOf("/img/");
101                         href = filePath.substring(index);
102                     } else {
103                         href = "";
104                     }
105                     return href;
106             }
107         }
108         return href;
109     }
110 
111     /**
112      * 獲取指定文件或文件路徑下的全部文件清單
113      *
114      * @param fileOrPath 文件或文件路徑
115      * @return
116      */
117     public static ArrayList<File> getListFiles(Object fileOrPath) {
118         File directory;
119         if (fileOrPath instanceof File) {
120             directory = (File) fileOrPath;
121         } else {
122             directory = new File(fileOrPath.toString());
123         }
124 
125         ArrayList<File> files = new ArrayList<File>();
126 
127         if (directory.isFile()) {
128             files.add(directory);
129             return files;
130         } else if (directory.isDirectory()) {
131             File[] fileArr = directory.listFiles();
132             if (null != fileArr && fileArr.length != 0) {
133                 for (File fileOne : fileArr) {
134                     files.addAll(getListFiles(fileOne));
135                 }
136             }
137         }
138 
139         return files;
140     }
141 
142 
143     /**
144      * 截圖工具,根據截取的比例進行縮放裁剪
145      *
146      * @param path        圖片路徑
147      * @param zoomX       縮放後的X座標
148      * @param zoomY       縮放後的Y座標
149      * @param zoomW       縮放後的截取寬度
150      * @param zoomH       縮放後的截取高度
151      * @param scaleWidth  縮放後圖片的寬度
152      * @param scaleHeight 縮放後的圖片高度
153      * @return 是否成功
154      * @throws Exception 任何異常均拋出
155      */
156     public static boolean imgCut(String path, int zoomX, int zoomY, int zoomW,
157                                  int zoomH, int scaleWidth, int scaleHeight) throws Exception {
158         Image img;
159         ImageFilter cropFilter;
160         BufferedImage bi = ImageIO.read(new File(path));
161         int fileWidth = bi.getWidth();
162         int fileHeight = bi.getHeight();
163         double scale = (double) fileWidth / (double) scaleWidth;
164 
165         double realX = zoomX * scale;
166         double realY = zoomY * scale;
167         double realW = zoomW * scale;
168         double realH = zoomH * scale;
169 
170         if (fileWidth >= realW && fileHeight >= realH) {
171             Image image = bi.getScaledInstance(fileWidth, fileHeight, Image.SCALE_DEFAULT);
172             cropFilter = new CropImageFilter((int) realX, (int) realY, (int) realW, (int) realH);
173             img = Toolkit.getDefaultToolkit().createImage(
174                     new FilteredImageSource(image.getSource(), cropFilter));
175             BufferedImage bufferedImage = new BufferedImage((int) realW, (int) realH, BufferedImage.TYPE_INT_RGB);
176             Graphics g = bufferedImage.getGraphics();
177             g.drawImage(img, 0, 0, null);
178             g.dispose();
179             //輸出文件
180             return ImageIO.write(bufferedImage, "JPEG", new File(path));
181         } else {
182             return true;
183         }
184     }
185 }

  順便一提:getWebRootPath這個方法,要生效,必須在Web.xml中作一個配置:

1  <context-param>
2         <param-name>webAppRootKey</param-name>
3         <param-value>web.root</param-value>
4     </context-param>

  不然是沒法動態獲取項目的本地路徑的。這個配置只要跟在Spring配置後面就好了,應該就不會有什麼大礙,其實就是獲取本地路徑而後設置到系統參數當中。

  好了,這就是整個插件的功能了。

 

附上Github地址:


https://github.com/wuxinzhe/Portrait.git 若是有幫助,但願給個Star

相關文章
相關標籤/搜索