前端代碼: css
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="x-ua-compatible" content="ie=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content="A basic demo of Cropper."> <meta name="keywords" content="HTML, CSS, JS, JavaScript, jQuery plugin, image cropping, image crop, image move, image zoom, image rotate, image scale, front-end, frontend, web development"> <meta name="author" content="Fengyuan Chen"> <title>Cropper Test</title> <link rel="stylesheet" href="../dist/cropper.css"> <style> .upload-img { width: 35%; overflow: hidden; margin-right: 20px; } .upload-preview,.upload-opr { overflow: hidden; } .upload-preview { margin-top: 50px; } .img-preview { float: left; overflow: hidden; border: 1px solid #7E7E7E; margin-right: 10px; } </style> </head> <body> <div class="upload-opr"> <button id="upload-btn" type="button" style="display: none;">上傳圖片</button> <button id="enable-cropper" type="button" style="display: none;">從新裁剪</button> <input type="file" id="select-image" accept="image/*" /> </div> <div class="upload-img"> <img src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/+IH6ElDQ19QUk9GSUxFAAEBAAAH2GFwcGwCIAAAbW50clJHQiBYWVogB9kAAgAZAAsAGgALYWNzcEFQUEwAAAAAYXBwbAAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALZGVzYwAAAQgAAABvZHNjbQAAAXgAAAWcY3BydAAABxQAAAA4d3RwdAAAB0wAAAAUclhZWgAAB2AAAAAUZ1hZWgAAB3QAAAAUYlhZWgAAB4gAAAAUclRSQwAAB5wAAAAOY2hhZAAAB6wAAAAsYlRSQwAAB5wAAAAOZ1RSQwAAB5wAAAAOZGVzYwAAAAAAAAAUR2VuZXJpYyBSR0IgUHJvZmlsZQAAAAAAAAAAAAAAFEdlbmVyaWMgUkdCIFByb2ZpbGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1sdWMAAAAAAAAAHwAAAAxza1NLAAAAKAAAAYRkYURLAAAALgAAAaxjYUVTAAAAJAAAAdp2aVZOAAAAJAAAAf5wdEJSAAAAJgAAAiJ1a1VBAAAAKgAAAkhmckZVAAAAKAAAAnJodUhVAAAAKAAAApp6aFRXAAAAFgAAAsJuYk5PAAAAJgAAAthjc0NaAAAAIgAAAv5oZUlMAAAAHgAAAyBpdElUAAAAKAAAAz5yb1JPAAAAJAAAA2ZkZURFAAAALAAAA4prb0tSAAAAFgAAA7ZzdlNFAAAAJgAAAth6aENOAAAAFgAAA8xqYUpQAAAAGgAAA+JlbEdSAAAAIgAAA/xwdFBPAAAAJgAABB5ubE5MAAAAKAAABERlc0VTAAAAJgAABB50aFRIAAAAJAAABGx0clRSAAAAIgAABJBmaUZJAAAAKAAABLJockhSAAAAKAAABNpwbFBMAAAALAAABQJydVJVAAAAIgAABS5hckVHAAAAJgAABVBlblVTAAAAJgAABXYAVgFhAGUAbwBiAGUAYwBuAP0AIABSAEcAQgAgAHAAcgBvAGYAaQBsAEcAZQBuAGUAcgBlAGwAIABSAEcAQgAtAGIAZQBzAGsAcgBpAHYAZQBsAHMAZQBQAGUAcgBmAGkAbAAgAFIARwBCACAAZwBlAG4A6AByAGkAYwBDHqUAdQAgAGgA7ABuAGgAIABSAEcAQgAgAEMAaAB1AG4AZwBQAGUAcgBmAGkAbAAgAFIARwBCACAARwBlAG4A6QByAGkAYwBvBBcEMAQzBDAEOwRMBD0EOAQ5ACAEPwRABD4ERAQwBDkEOwAgAFIARwBCAFAAcgBvAGYAaQBsACAAZwDpAG4A6QByAGkAcQB1AGUAIABSAFYAQgDBAGwAdABhAGwA4QBuAG8AcwAgAFIARwBCACAAcAByAG8AZgBpAGyQGnUoACAAUgBHAEIAIIJyX2ljz4/wAEcAZQBuAGUAcgBpAHMAawAgAFIARwBCAC0AcAByAG8AZgBpAGwATwBiAGUAYwBuAP0AIABSAEcAQgAgAHAAcgBvAGYAaQBsBeQF6AXVBeQF2QXcACAAUgBHAEIAIAXbBdwF3AXZAFAAcgBvAGYAaQBsAG8AIABSAEcAQgAgAGcAZQBuAGUAcgBpAGMAbwBQAHIAbwBmAGkAbAAgAFIARwBCACAAZwBlAG4AZQByAGkAYwBBAGwAbABnAGUAbQBlAGkAbgBlAHMAIABSAEcAQgAtAFAAcgBvAGYAaQBsx3y8GAAgAFIARwBCACDVBLhc0wzHfGZukBoAIABSAEcAQgAgY8+P8GWHTvZOAIIsACAAUgBHAEIAIDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA8ADwQO/A8YDrwO7ACAAUgBHAEIAUABlAHIAZgBpAGwAIABSAEcAQgAgAGcAZQBuAOkAcgBpAGMAbwBBAGwAZwBlAG0AZQBlAG4AIABSAEcAQgAtAHAAcgBvAGYAaQBlAGwOQg4bDiMORA4fDiUOTAAgAFIARwBCACAOFw4xDkgOJw5EDhsARwBlAG4AZQBsACAAUgBHAEIAIABQAHIAbwBmAGkAbABpAFkAbABlAGkAbgBlAG4AIABSAEcAQgAtAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAFIARwBCACAAcAByAG8AZgBpAGwAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABSAEcAQgQeBDEESQQ4BDkAIAQ/BEAEPgREBDgEOwRMACAAUgBHAEIGRQZEBkEAIAYqBjkGMQZKBkEAIABSAEcAQgAgBicGRAY5BicGRQBHAGUAbgBlAHIAaQBjACAAUgBHAEIAIABQAHIAbwBmAGkAbABldGV4dAAAAABDb3B5cmlnaHQgMjAwNyBBcHBsZSBJbmMuLCBhbGwgcmlnaHRzIHJlc2VydmVkLgBYWVogAAAAAAAA81IAAQAAAAEWz1hZWiAAAAAAAAB0TQAAPe4AAAPQWFlaIAAAAAAAAFp1AACscwAAFzRYWVogAAAAAAAAKBoAABWfAAC4NmN1cnYAAAAAAAAAAQHNAABzZjMyAAAAAAABDEIAAAXe///zJgAAB5IAAP2R///7ov///aMAAAPcAADAbP/AABEIAMgAyAMBIgACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2wBDAAICAgICAgMCAgMFAwMDBQYFBQUFBggGBgYGBggKCAgICAgICgoKCgoKCgoMDAwMDAwODg4ODg8PDw8PDw8PDw//2wBDAQICAgQEBAcEBAcQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/3QAEAA3/2gAMAwEAAhEDEQA/AP1AoooryzQKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//9D9QKKKK8s0CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP//R/UCiiivLNAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//0v1AoooryzQKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//9P9QKKKK8s0CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP//U/UCiiivLNAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//1f1AoooryzQKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//9b9QKKKK8s0CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP//X/UCiiivLNAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//0P1AoooryzQKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//9H9QKKKK8s0CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP//S/UCiiivLNAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//0/1AoooryzQKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//9k="/> </div> <div class="upload-preview"> <div class="img-preview" style="width: 400px;height: 300px;"></div> <div class="img-preview" style="width: 240px;height: 180px;"></div> <div class="img-preview" style="width: 120px;height: 90px;"></div> </div> <!-- Scripts --> <script src="../assets/js/jquery.min.js"></script> <script src="../dist/cropper.js"></script> <script src="../assets/js/async.js"></script> <script> $(function(){ var $image = $('.upload-img img'); $image.cropper({ preview: '.img-preview', //不一樣尺寸預覽區 aspectRatio: 4/3, //裁剪比例,NaN-自由選擇區域 autoCropArea: 0.9, //初始裁剪區域佔圖片比例 crop: function(data) { //裁剪操做回調 } }); var fileName; //選擇上傳的文件名 $('#select-image').change(function(){ var file = this.files[0]; fileName = file.name; var reader = new FileReader(); //reader回調,從新初始裁剪區 reader.onload = function(){ // 經過 reader.result 來訪問生成的 DataURL var url = reader.result; //選擇圖片後從新初始裁剪區 $('.upload-img img').attr('src', url); $image.cropper('reset', true).cropper('replace', url); //顯示上傳按鈕 $('#upload-btn').show(); //隱藏從新裁剪按鈕 $('#enable-cropper').hide(); }; reader.readAsDataURL(file); }); /*解除裁剪區鎖定*/ $('#enable-cropper').click(function() { $image.cropper('enable'); $('#upload-btn').show(); $(this).hide(); }); /* * 上傳圖片 */ $('#upload-btn').click(function() { var type = $image.attr('src').split(';')[0].split(':')[1]; //series 中的方法順序執行裁剪不一樣尺寸的圖片獲得blob後調用callback上傳圖片 async.series({ '_': function(callback) { $image.cropper("getCroppedCanvas").toBlob(function(blob) { callback(null, blob); }, type); }, '400x300': function(callback) { $image.cropper("getCroppedCanvas", { "width": 400, "height": 300 }).toBlob(function(blob) { callback(null, blob); }, type); }, '240x180': function(callback) { $image.cropper("getCroppedCanvas", { "width": 240, "height": 180 }).toBlob(function(blob) { callback(null, blob); }, type); }, '120x90': function(callback) { $image.cropper("getCroppedCanvas", { "width": 120, "height": 90 }).toBlob(function(blob) { callback(null, blob); }, type); } }, //異步最終回調,上傳圖片 function(err, results){ //results: {'_', blob Object, '400x300': blob Object, '240x180': blob Object, ...} console.log("results", JSON.stringify(results)); var formData = new FormData(); for(var key in results) { var wh = key.split('x',2); var sizeName = fileName; if(wh.length == 2) { var idx = fileName.lastIndexOf('.') sizeName = fileName.substring(0, idx)+'_' + key + fileName.substring(idx) } //把要上傳的圖片blob加到formData中 formData.append("file", results[key], sizeName); } $.ajax({ type: "POST", url: '/multiUpload', data: formData, contentType: false, //必須 processData: false, //必須 dataType: "json", success: function(retJson){ //上傳成功回調函數 if(uploadCallback && typeof uploadCallback == 'function') { uploadCallback(retJson); } else { console.log(JSON.stringify(retJson)); } if(retJson.code == 0) { $('#upload-btn').hide(); $image.cropper("disable"); $('#enable-cropper').show(); } }, error : function() { $('#upload-btn').hide(); $image.cropper("disable"); $('#enable-cropper').show(); console.log('failed'); alert('上傳失敗'); }, beforeSend: function(){ }, complete: function(){ } }); }); }); }) </script> </body> </html>
服務端代碼:結合Spring MVC實現上傳,關於spring mvc上傳網上不少,配置就不詳細說明了html
@RestController public class MainController { private static SimpleDateFormat df = new SimpleDateFormat("YYYY/MM/dd/"); private String uploadUrlPrefix = "http://localhost/upload"; private String uploadDir = "/data/upload"; @RequestMapping(value = "/multiUpload", name = "批量上傳圖片") public String uploadMulti(@RequestParam("file") CommonsMultipartFile[] files) throws IOException { JSONObject json = new JSONObject(); if(files == null) { json.put("code", -1); json.put("message", "沒有上傳文件"); return json.toJSONString(); } JSONArray jsonArray = new JSONArray(); json.put("data", jsonArray); String uuid = UUID.randomUUID().toString(); for(CommonsMultipartFile file : files) { String oriFileName = file.getOriginalFilename(); StringBuilder newFileName = new StringBuilder(); boolean isBase = false; if(oriFileName.matches(".*_\\d+x\\d+\\.\\w+")) { newFileName.append(oriFileName.replaceFirst("(.*)(_\\d+x\\d+\\.\\w+)", uuid+"$2")); } else { newFileName.append(uuid).append(oriFileName.substring(oriFileName.lastIndexOf('.'))); isBase = true; } String timeDir = df.format(new Date()); String imgPath = uploadDir + File.separatorChar + timeDir + fileName; log.info("save file name: {}", newFileName); File imgFile = new File(imgPath); if(!imgFile.getParentFile().exists()) { imgFile.getParentFile().mkdirs(); } //保存文件 file.transferTo(imgFile); JSONObject imgJson = new JSONObject(); imgJson.put("oriFileName", oriFileName); imgJson.put("state", "SUCCESS"); imgJson.put("url", uploadUrlPrefix + "/" + uri); jsonArray.add(imgJson); if(isBase) { json.put("baseUrl", imgJson.getString("url")); } } json.put("code", 0); json.put("message", "上傳成功"); return json.toJSONString(); } }
cropper js 參考:https://fengyuanchen.github.io/cropper/前端
async異步庫參考:https://github.com/caolan/asyncjava
大致思路:jquery
效果:git