前端代码:
<!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="https://img-blog.csdnimg.cn/2022010707204559759.jpeg"/>
</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上传网上很多,配置就不详细说明了
@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/async
大体思路:
- 用cropper js 实现图片预览,裁剪
- 调用cropper js的getCroppedCanvas来获取不同尺寸的图片canvas
- 结合async 实现顺序地从canvas.toBlob的回调中获取到blob,很好的解决了异步的问题,这点非常重要
- 用FormData装载图片,ajax上传
效果: