日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

[Bootstrap-插件使用]Jcrop+fileinput组合实现头像上传功能

發(fā)布時(shí)間:2024/4/15 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [Bootstrap-插件使用]Jcrop+fileinput组合实现头像上传功能 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

很久沒有更新博客了,再不寫點(diǎn)東西都爛了。

這次更新一個(gè)小內(nèi)容,是兩個(gè)插件的組合使用,實(shí)現(xiàn)頭像上傳功能。

業(yè)務(wù)需求:


  • 頭像上傳功能,要對(duì)上傳的文件進(jìn)行剪切,且保證頭像到服務(wù)器時(shí)必須是正方形的。
  • 優(yōu)化<input type="file">的顯示樣式,基礎(chǔ)的樣式實(shí)在太難看了。
  • 上傳的頭像需要進(jìn)行質(zhì)量壓縮跟大小裁剪,以減緩瀏覽器的壓力。

成果預(yù)覽:

?

使用到的技術(shù)插件


  • Jcrop:用于前端“裁剪”圖片

  • bootstrap-fileinput:用于前端優(yōu)化上傳控件樣式
  • ARTtemplate:JS版的JSTL?反正就是一個(gè)騰訊的模板化插件,很好用,真心。
  • bootstrap-sco.modal.js:這個(gè)是bootstrap的一個(gè)模態(tài)插件

  • SpringMVC:使用框架自帶的MultipartFile來獲取文件(效率能夠大大的提高)

  • Image:這個(gè)是Java的內(nèi)置類,用于處理圖片很方便。

原理說明


?

  首先是Jcrop這個(gè)前端JS插件,這個(gè)插件很好用,其實(shí)在各大網(wǎng)站中也很常見,先上圖:

沒錯(cuò),這個(gè)一臉懵逼的就是我。。。

  說說原理,實(shí)際上,Jcrop并沒有在客戶端幫我們把圖片進(jìn)行裁剪,只是收集了用戶的“裁剪信息”,然后傳到后端,最后的裁剪和壓縮,還是要依靠服務(wù)器上的代碼來進(jìn)行。

  我們可以看到這個(gè)插件在圖片上顯示出了8個(gè)控制點(diǎn),讓用戶選擇裁剪區(qū)域,當(dāng)用戶選擇成功后,會(huì)自動(dòng)的返回“裁剪信息”,所謂的裁剪信息,其實(shí)就是選框的左上角原點(diǎn)坐標(biāo),及裁剪框的寬度和高度,通過這四個(gè)值,在后端就可以進(jìn)行裁剪工作了。

  但是我們要注意,用戶在上傳圖片的時(shí)候,長(zhǎng)度寬度都是不規(guī)則的,當(dāng)然我們可以用bootstap-fileinput這個(gè)插件去限制用戶只能上傳指定寬高的圖片,但這就失去了我們“裁剪”的意義,而且用戶的體驗(yàn)就非常差勁。然而jcrop所返回的坐標(biāo)值及寬高,并不是基于所上傳圖片自身的像素,而是如圖中所示,是外層DIV的寬高。舉一個(gè)例子,上圖我實(shí)際放入的個(gè)人照片寬度是852px,但是Jcrop的截取寬度是312px,這個(gè)312px并不是真正圖片上的實(shí)際寬度,是經(jīng)過縮放后的寬度,所以我們后端一定需要重新對(duì)這個(gè)312px進(jìn)行一次還原,還原到照片實(shí)際比例的寬度。

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

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>

  這個(gè)就是一個(gè)ArtTemplate的模板代碼,就寫在</body>標(biāo)簽上方就行了,因?yàn)閠ext/html這個(gè)類型,不會(huì)被識(shí)別,所以實(shí)際上用Chrome調(diào)試就可以看得到,前端用戶是看不到這段代碼的。

  簡(jiǎn)單解釋一下這個(gè)模板,這個(gè)模板是我最后放入模態(tài)窗口時(shí)用的模板,就是把這段代碼,直接丟進(jìn)模態(tài)彈出來的內(nèi)容部分。因?yàn)槭俏募蟼?#xff0c;自然需要套一個(gè)<form>標(biāo)簽,然后必須給form標(biāo)簽放入 enctype="multipart/form-data",否則后端Spring就無法獲取這個(gè)文件。

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

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

  pre-scrollable這個(gè)類,會(huì)讓加載的圖片不會(huì)因?yàn)樘蠖白冃巍?#xff0c;因?yàn)槲彝鈱油ㄟ^embed-responsive-16by9限制死了圖片的寬高,圖片本身又加了img-responsive這個(gè)添加響應(yīng)式屬性的類,為了防止圖片縮放,導(dǎo)致截圖障礙,所以就給內(nèi)層加上pre-scrollable,這個(gè)會(huì)給圖片這一層div加上滾動(dòng)條,如果圖片高度太高,超過了外層框,則會(huì)出現(xiàn)滾動(dòng)條,而這不會(huì)影響圖片截取,同時(shí)又保證了模態(tài)窗口不會(huì)“太長(zhǎng)”,導(dǎo)致體驗(yàn)糟糕(尤其在移動(dòng)端)。

  底下四個(gè)隱藏域相信大家看他們的name值也就知道個(gè)大概,這個(gè)就是用于存放Jcrop截取時(shí)所產(chǎn)生的原點(diǎn)坐標(biāo)和截取寬高的值。

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 }

  這個(gè)JS是上傳頁(yè)面的相關(guān)邏輯。會(huì)JS的人都看得懂它的意義,我就簡(jiǎn)單說一下幾個(gè)事件的意義:

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

  這個(gè)事件,是用于bootstrap-fileinput插件在校驗(yàn)文件格式、文件大小等的時(shí)候,如果不符合我們的要求,則會(huì)對(duì)前面HTML代碼中有一個(gè)

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

  進(jìn)行一些錯(cuò)誤信息的顯示操作。

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

  這部分代碼,是當(dāng)文件移除時(shí),隱藏錯(cuò)誤信息提示區(qū),以及清空內(nèi)容,當(dāng)然這是符合我們的業(yè)務(wù)邏輯的。

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

  這部分代碼是當(dāng)選擇文件時(shí)(此時(shí)還沒進(jìn)行文件校驗(yàn)),隱藏錯(cuò)誤信息,清空錯(cuò)誤內(nèi)容,這么做是為了應(yīng)對(duì)如果上一次文件校驗(yàn)時(shí)有錯(cuò)誤,而重新選擇文件時(shí),肯定要清空上一次的錯(cuò)誤信息,再顯示本次的錯(cuò)誤信息。

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

  這部分是當(dāng)文件上傳后,后端如果返回了錯(cuò)誤信息,則需要進(jìn)行相關(guān)的提示信息處理。

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 }

  這部分代碼是獲取上傳文件時(shí),附帶需要發(fā)往后端的參數(shù),這里面可以看到,x、y自然是Jcrop截取時(shí),選框的左上角原點(diǎn)坐標(biāo),w、h自然就是截取的寬高,但是剛才我說了,這個(gè)是經(jīng)過縮放后的寬高,不是依據(jù)圖片實(shí)際像素的寬高。而sw、sh代表的是scaleWidth、scaleHeight,就是縮放寬高的意思。這個(gè).jcrop-holder的對(duì)象是當(dāng)Jcrop插件啟用后,加載的圖片外層容器的對(duì)象,只需要獲取這個(gè)對(duì)象的寬高,就是圖片被壓縮的寬高,但是因?yàn)槲蚁拗屏藞D片的寬度和高度,寬度的比例是定死的(不是寬高定死,只是比例定死,bootstrap本身就是響應(yīng)式框架,所以不能單純的說寬高定死,寬高會(huì)隨著使用終端的變化而變化),高度是根據(jù)寬度保持16:4,可是我又加了pre-scrollable這個(gè)類讓圖片過高時(shí)以滾動(dòng)條的方式不破壞外層容器的高度,所以我們實(shí)際能拿來計(jì)算縮放比例的,是寬度,而不是高度,但是這里我一起傳,萬一以后有其他的使用場(chǎng)景,要以高度為準(zhǔn)也說不定。

  好了,然后我需要貼上bootstrap-fileinput插件的配置代碼:

1 this.portrait = function (target, uploadUrl, data, alert) { 2 target.fileinput({ 3 language: 'zh', //設(shè)置語言 4 maxFileSize: 2048,//文件最大容量 5 uploadExtraData: data,//上傳時(shí)除了文件以外的其他額外數(shù)據(jù) 6 showPreview: false,//隱藏預(yù)覽 7 uploadAsync: true,//ajax同步 8 dropZoneEnabled: false,//是否顯示拖拽區(qū)域 9 uploadUrl: uploadUrl, //上傳的地址 10 elErrorContainer: alert,//錯(cuò)誤信息內(nèi)容容器 11 allowedFileExtensions: ['jpg'],//接收的文件后綴 12 showUpload: true, //是否顯示上傳按鈕 13 showCaption: true,//是否顯示標(biāo)題 14 browseClass: "btn btn-primary", //按鈕樣式 15 previewFileIcon: "<i class='glyphicon glyphicon-king'></i>", 16 ajaxSettings: {//這個(gè)是因?yàn)槲沂褂昧薙pringSecurity框架,有csrf跨域提交防御,所需需要設(shè)置這個(gè)值 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 };

  這個(gè)代碼有寫了注釋,我就不多解釋了。

  唯一做一個(gè)補(bǔ)充,就是“elErrorContainer: alert,//錯(cuò)誤信息內(nèi)容容器”這個(gè)配置,這個(gè)是我后來再次研究這個(gè)插件得到的一個(gè)心得,這個(gè)插件自帶錯(cuò)誤信息顯示的功能,但是吧,至少我還不知道如何能夠讓ajax后自定義的錯(cuò)誤信息調(diào)用這個(gè)顯示功能,于是我就只能自己定義一個(gè)alert的容器,用來存放錯(cuò)誤信息來擴(kuò)展這個(gè)插件,但是這樣就會(huì)在某種情況下比如400錯(cuò)誤時(shí),導(dǎo)致出現(xiàn)兩個(gè)錯(cuò)誤信息提示,那解決的辦法我是看到了這個(gè)參數(shù),只需要將這個(gè)錯(cuò)誤信息的容器從默認(rèn)值修改為我自定義的容器就可以了。

  關(guān)于Ajax同步,是因?yàn)槲覀€(gè)人認(rèn)為,上傳文件這個(gè)還是做成同步比較好,等文件上傳完成后,js代碼才能繼續(xù)執(zhí)行下去。因?yàn)槲募蟼鳟吘故且粋€(gè)耗時(shí)的工作,有的邏輯又確實(shí)需要當(dāng)文件上傳成功以后才執(zhí)行,比如刷新頁(yè)面,所以為了避免出現(xiàn)問題,還是做成同步的比較好。還有就是去掉預(yù)覽,用過bootstrap-fileinput插件的都知道,這個(gè)插件的圖片預(yù)覽功能很強(qiáng)大,甚至可以單獨(dú)依靠這個(gè)插件來制作相冊(cè)管理。但是因?yàn)槲覀冞@次要結(jié)合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用于指向?qū)?yīng)文件夾 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("圖片壓縮失敗!請(qǐng)重新上傳!"); 50 } 51 } else { 52 return JsonResult.error("圖片截取失敗!請(qǐng)重新上傳!"); 53 } 54 } 55 } 56 } 57 return JsonResult.error("圖片獲取失敗!請(qǐng)重新上傳!"); 58 }

Image圖片切割


?

1 /** 2 * 截圖工具,根據(jù)截取的比例進(jìn)行縮放裁剪 3 * 4 * @param path 圖片路徑 5 * @param zoomX 縮放后的X坐標(biāo) 6 * @param zoomY 縮放后的Y坐標(biāo) 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,并且寬高也要轉(zhuǎn)換成double后再相除,否則會(huì)變成求模運(yùn)算,這樣會(huì)降低精度,別小看這里的精度下降,最終的截圖效果根據(jù)圖片的縮放程度,誤差可是有可能被放大的很離譜的。

?

圖片壓縮


1 package com.magic.rent.tools; 2 3 /** 4 * 知識(shí)產(chǎn)權(quán)聲明:本文件自創(chuàng)建起,其內(nèi)容的知識(shí)產(chǎn)權(quán)即歸屬于原作者,任何他人不可擅自復(fù)制或模仿. 5 * 創(chuàng)建者: wu 創(chuàng)建時(shí)間: 2016/12/15 6 * 類說明: 縮略圖類(通用) 本java類能將jpg、bmp、png、gif圖片文件,進(jìn)行等比或非等比的大小轉(zhuǎn)換。 具體使用方法 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; // 文件對(duì)象 24 private String inputDir; // 輸入圖路徑 25 private String outputDir; // 輸出圖路徑 26 private String inputFileName; // 輸入圖文件名 27 private String outputFileName; // 輸出圖文件名 28 private int outputWidth = 100; // 默認(rèn)輸出圖片寬 29 private int outputHeight = 100; // 默認(rèn)輸出圖片高 30 private boolean proportion = true; // 是否等比縮放標(biāo)記(默認(rèn)為等比縮放) 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 * 設(shè)置輸入?yún)?shù) 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 * 設(shè)置輸出參數(shù) 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 // 為等比縮放計(jì)算輸出的圖片寬度及高度 92 double rate1 = ((double) img.getWidth(null)) / (double) outputWidth + 0.1; 93 double rate2 = ((double) img.getHeight(null)) / (double) outputHeight + 0.1; 94 // 根據(jù)縮放比率大的進(jìn)行縮放控制 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 * 優(yōu)先級(jí)比速度高 生成的圖片質(zhì)量比較好 但速度慢 107 */ 108 tag.getGraphics().drawImage(img.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH), 0, 0, null); 109 FileOutputStream out = new FileOutputStream(outputDir); 110 111 // JPEGImageEncoder可適用于其他圖片類型的轉(zhuǎn)換 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[耗時(shí)]:" + time + "毫秒"); 117 return true; 118 } 119 } 120 121 122 /** 123 * 簡(jiǎn)單壓縮方法,壓縮后圖片將直接覆蓋源文件 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 }

  我專門把圖片壓縮寫成了一個(gè)類。

  其中可以看到一些關(guān)于文件路徑的方法,其實(shí)沒有什么特別的,就是截取后綴獲取路徑之類的,我這邊也貼出來吧,免得有些朋友看的云里霧里的。

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 * 知識(shí)產(chǎn)權(quán)聲明:本文件自創(chuàng)建起,其內(nèi)容的知識(shí)產(chǎn)權(quán)即歸屬于原作者,任何他人不可擅自復(fù)制或模仿. 16 * 創(chuàng)建者: wu 創(chuàng)建時(shí)間: 2016/11/25 17 * 類說明: 18 * 更新記錄: 19 */ 20 public class FileTools { 21 22 public static final int IMG = 1; 23 24 /** 25 * 獲取項(xiàng)目根目錄 26 * 27 * @return 根目錄 28 */ 29 public static String getWebRootPath() { 30 return System.getProperty("web.root"); 31 } 32 33 /** 34 * 獲取頭像目錄,若不存在則直接創(chuàng)建一個(gè) 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 //判斷文件夾是否存在,不存在則創(chuàng)建一個(gè) 43 if (!file.exists() || !file.isDirectory()) { 44 if (!file.mkdirs()) { 45 throw new BusinessException("創(chuàng)建頭像文件夾失敗!"); 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 * 將文件地址轉(zhuǎn)成鏈接地址 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 * 截圖工具,根據(jù)截取的比例進(jìn)行縮放裁剪 145 * 146 * @param path 圖片路徑 147 * @param zoomX 縮放后的X坐標(biāo) 148 * @param zoomY 縮放后的Y坐標(biāo) 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這個(gè)方法,要生效,必須在Web.xml中做一個(gè)配置:

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

  否則是無法動(dòng)態(tài)獲取項(xiàng)目的本地路徑的。這個(gè)配置只要跟在Spring配置后面就行了,應(yīng)該就不會(huì)有什么大礙,其實(shí)就是獲取本地路徑然后設(shè)置到系統(tǒng)參數(shù)當(dāng)中。

  好了,這就是整個(gè)插件的功能了。

?

附上Github地址:


https://github.com/wuxinzhe/Portrait.git 如果有幫助,希望給個(gè)Star

轉(zhuǎn)載于:https://www.cnblogs.com/wuxinzhe/p/6198506.html

總結(jié)

以上是生活随笔為你收集整理的[Bootstrap-插件使用]Jcrop+fileinput组合实现头像上传功能的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。