好久沒有更新博客了,再不寫點東西都爛了。css
此次更新一個小內容,是兩個插件的組合使用,實現頭像上傳功能。html
成果預覽:前端
Jcrop:用於前端「裁剪」圖片java
bootstrap-sco.modal.js:這個是bootstrap的一個模態插件git
SpringMVC:使用框架自帶的MultipartFile來獲取文件(效率可以大大的提升)github
首先是Jcrop這個前端JS插件,這個插件很好用,其實在各大網站中也很常見,先上圖:web
沒錯,這個一臉懵逼的就是我。。。ajax
說說原理,實際上,Jcrop並無在客戶端幫咱們把圖片進行裁剪,只是收集了用戶的「裁剪信息」,而後傳到後端,最後的裁剪和壓縮,仍是要依靠服務器上的代碼來進行。算法
咱們能夠看到這個插件在圖片上顯示出了8個控制點,讓用戶選擇裁剪區域,當用戶選擇成功後,會自動的返回「裁剪信息」,所謂的裁剪信息,其實就是選框的左上角原點座標,及裁剪框的寬度和高度,經過這四個值,在後端就能夠進行裁剪工做了。bootstrap
可是咱們要注意,用戶在上傳圖片的時候,長度寬度都是不規則的,固然咱們能夠用bootstap-fileinput這個插件去限制用戶只能上傳指定寬高的圖片,但這就失去了咱們「裁剪」的意義,並且用戶的體驗就很是差勁。然而jcrop所返回的座標值及寬高,並非基於所上傳圖片自身的像素,而是如圖中所示,是外層DIV的寬高。舉一個例子,上圖我實際放入的我的照片寬度是852px,可是Jcrop的截取寬度是312px,這個312px並非真正圖片上的實際寬度,是通過縮放後的寬度,因此咱們後端必定須要從新對這個312px進行一次還原,還原到照片實際比例的寬度。
好啦,原理就是這樣子。接下來,就是上代碼了。
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截取時所產生的原點座標和截取寬高的值。
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,因此要割掉這部分功能。
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 }
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配置後面就好了,應該就不會有什麼大礙,其實就是獲取本地路徑而後設置到系統參數當中。
好了,這就是整個插件的功能了。
https://github.com/wuxinzhe/Portrait.git 若是有幫助,但願給個Star