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

时间:2022-12-14 12:32:55

很久没有更新博客了,再不写点东西都烂了。

这次更新一个小内容,是两个插件的组合使用,实现头像上传功能。

业务需求:


  • 头像上传功能,要对上传的文件进行剪切,且保证头像到服务器时必须是正方形的。
  • 优化<input type="file">的显示样式,基础的样式实在太难看了。
  • 上传的头像需要进行质量压缩跟大小裁剪,以减缓浏览器的压力。

成果预览:

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

使用到的技术插件


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

  • bootstrap-fileinput:用于前端优化上传控件样式
  • ARTtemplate:JS版的JSTL?反正就是一个腾讯的模板化插件,很好用,真心。
  • bootstrap-sco.modal.js:这个是bootstrap的一个模态插件

  • SpringMVC:使用框架自带的MultipartFile来获取文件(效率能够大大的提高)

  • Image:这个是Java的内置类,用于处理图片很方便。

原理说明


  首先是Jcrop这个前端JS插件,这个插件很好用,其实在各大网站中也很常见,先上图:

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

没错,这个一脸懵逼的就是我。。。

  说说原理,实际上,Jcrop并没有在客户端帮我们把图片进行裁剪,只是收集了用户的“裁剪信息”,然后传到后端,最后的裁剪和压缩,还是要依靠服务器上的代码来进行。

  我们可以看到这个插件在图片上显示出了8个控制点,让用户选择裁剪区域,当用户选择成功后,会自动的返回“裁剪信息”,所谓的裁剪信息,其实就是选框的左上角原点坐标,及裁剪框的宽度和高度,通过这四个值,在后端就可以进行裁剪工作了。

  但是我们要注意,用户在上传图片的时候,长度宽度都是不规则的,当然我们可以用bootstap-fileinput这个插件去限制用户只能上传指定宽高的图片,但这就失去了我们“裁剪”的意义,而且用户的体验就非常差劲。然而jcrop所返回的坐标值及宽高,并不是基于所上传图片自身的像素,而是如图中所示,是外层DIV的宽高。举一个例子,上图我实际放入的个人照片宽度是852px,但是Jcrop的截取宽度是312px,这个312px并不是真正图片上的实际宽度,是经过缩放后的宽度,所以我们后端一定需要重新对这个312px进行一次还原,还原到照片实际比例的宽度。

  好啦,原理就是这样子。接下来,就是上代码了。

HTML


 <script id="portraitUpload" type="text/html">
<div style="padding: 10px 20px">
<form role="form" enctype="multipart/form-data" method="post">
<div class="embed-responsive embed-responsive-16by9">
<div class="embed-responsive-item pre-scrollable">
<img alt="" src="${pageContext.request.contextPath}/img/showings.jpg" id="cut-img"
class="img-responsive img-thumbnail"/>
</div>
</div>
<div class="white-divider-md"></div>
<input type="file" name="imgFile" id="fileUpload"/>
<div class="white-divider-md"></div>
<div id="alert" class="alert alert-danger hidden" role="alert"></div>
<input type="hidden" id="x" name="x"/>
<input type="hidden" id="y" name="y"/>
<input type="hidden" id="w" name="w"/>
<input type="hidden" id="h" name="h"/>
</form>
</div>
</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


 $(document).ready(function () {
new PageInit().init();
}); function PageInit() {
var api = null;
var _this = this;
this.init = function () {
$("[name='upload']").on('click', this.portraitUpload);
}; this.portraitUpload = function () {
var model = $.scojs_modal({
title: '头像上传',
content: template('portraitUpload'),
onClose: refresh
}
);
model.show();
var fileUp = new FileUpload();
var portrait = $('#fileUpload');
fileUp.portrait(portrait, '/upload/portrait', _this.getExtraData, $('#alert'));
portrait.on('change', _this.readURL);
}; this.readURL = function () {
var img = $('#cut-img');
var input = $('#fileUpload');
if (input[0].files && input[0].files[0]) {
var reader = new FileReader();
reader.readAsDataURL(input[0].files[0]);
reader.onload = function (e) {
img.removeAttr('src');
img.attr('src', e.target.result);
img.Jcrop({
setSelect: [20, 20, 200, 200],
handleSize: 10,
aspectRatio: 1,
onSelect: updateCords
}, function () {
api = this;
});
};
if (api != undefined) {
api.destroy();
}
}
function updateCords(obj) {
$("#x").val(obj.x);
$("#y").val(obj.y);
$("#w").val(obj.w);
$("#h").val(obj.h);
}
}; this.getExtraData = function () {
return {
sw: $('.jcrop-holder').css('width'),
sh: $('.jcrop-holder').css('height'),
x: $('#x').val(),
y: $('#y').val(),
w: $('#w').val(),
h: $('#h').val()
}
}
}

  这个JS是上传页面的相关逻辑。会JS的人都看得懂它的意义,我就简单说一下几个事件的意义:

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

  这个事件,是用于bootstrap-fileinput插件在校验文件格式、文件大小等的时候,如果不符合我们的要求,则会对前面HTML代码中有一个

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

  进行一些错误信息的显示操作。

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

  这部分代码,是当文件移除时,隐藏错误信息提示区,以及清空内容,当然这是符合我们的业务逻辑的。

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

  这部分代码是当选择文件时(此时还没进行文件校验),隐藏错误信息,清空错误内容,这么做是为了应对如果上一次文件校验时有错误,而重新选择文件时,肯定要清空上一次的错误信息,再显示本次的错误信息。

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

  这部分是当文件上传后,后端如果返回了错误信息,则需要进行相关的提示信息处理。

  this.getExtraData = function () {
return {
sw: $('.jcrop-holder').css('width'),
sh: $('.jcrop-holder').css('height'),
x: $('#x').val(),
y: $('#y').val(),
w: $('#w').val(),
h: $('#h').val()
}
}

  这部分代码是获取上传文件时,附带需要发往后端的参数,这里面可以看到,x、y自然是Jcrop截取时,选框的左上角原点坐标,w、h自然就是截取的宽高,但是刚才我说了,这个是经过缩放后的宽高,不是依据图片实际像素的宽高。而sw、sh代表的是scaleWidth、scaleHeight,就是缩放宽高的意思。这个.jcrop-holder的对象是当Jcrop插件启用后,加载的图片外层容器的对象,只需要获取这个对象的宽高,就是图片被压缩的宽高,但是因为我限制了图片的宽度和高度,宽度的比例是定死的(不是宽高定死,只是比例定死,bootstrap本身就是响应式框架,所以不能单纯的说宽高定死,宽高会随着使用终端的变化而变化),高度是根据宽度保持16:4,可是我又加了pre-scrollable这个类让图片过高时以滚动条的方式不破坏外层容器的高度,所以我们实际能拿来计算缩放比例的,是宽度,而不是高度,但是这里我一起传,万一以后有其他的使用场景,要以高度为准也说不定。

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

 this.portrait = function (target, uploadUrl, data, alert) {
target.fileinput({
language: 'zh', //设置语言
maxFileSize: 2048,//文件最大容量
uploadExtraData: data,//上传时除了文件以外的其他额外数据
showPreview: false,//隐藏预览
uploadAsync: true,//ajax同步
dropZoneEnabled: false,//是否显示拖拽区域
uploadUrl: uploadUrl, //上传的地址
elErrorContainer: alert,//错误信息内容容器
allowedFileExtensions: ['jpg'],//接收的文件后缀
showUpload: true, //是否显示上传按钮
showCaption: true,//是否显示标题
browseClass: "btn btn-primary", //按钮样式
previewFileIcon: "<i class='glyphicon glyphicon-king'></i>",
ajaxSettings: {//这个是因为我使用了SpringSecurity框架,有csrf跨域提交防御,所需需要设置这个值
beforeSend: function (xhr) {
xhr.setRequestHeader(header, token);
}
}
});
this.alert(target, alert);
};
this.alert = function (target, alert) {
target.on('fileuploaderror', function (event, data, msg) {
alert.removeClass('hidden').html(msg);
_this.fileinput('disable');
});
target.on('fileclear', function (event) {
alert.addClass('hidden').html();
});
target.on('fileloaded', function (event, file, previewId, index, reader) {
alert.addClass('hidden').html();
});
target.on('fileuploaded', function (event, data) {
if (!data.response.status) {
alert.html(data.response.message).removeClass('hidden');
}
});
};

  这个代码有写了注释,我就不多解释了。

  唯一做一个补充,就是“elErrorContainer: alert,//错误信息内容容器”这个配置,这个是我后来再次研究这个插件得到的一个心得,这个插件自带错误信息显示的功能,但是吧,至少我还不知道如何能够让ajax后自定义的错误信息调用这个显示功能,于是我就只能自己定义一个alert的容器,用来存放错误信息来扩展这个插件,但是这样就会在某种情况下比如400错误时,导致出现两个错误信息提示,那解决的办法我是看到了这个参数,只需要将这个错误信息的容器从默认值修改为我自定义的容器就可以了。

  关于Ajax同步,是因为我个人认为,上传文件这个还是做成同步比较好,等文件上传完成后,js代码才能继续执行下去。因为文件上传毕竟是一个耗时的工作,有的逻辑又确实需要当文件上传成功以后才执行,比如刷新页面,所以为了避免出现问题,还是做成同步的比较好。还有就是去掉预览,用过bootstrap-fileinput插件的都知道,这个插件的图片预览功能很强大,甚至可以单独依靠这个插件来制作相册管理。但是因为我们这次要结合Jcrop,所以要割掉这部分功能。

SpringMVC-Controller获取文件


 @ResponseBody
@RequestMapping(value = "/portrait", method = {RequestMethod.POST})
public JsonResult upload(HttpServletRequest request) throws Exception {
Integer x = Integer.parseInt(MyStringTools.checkParameter(request.getParameter("x"), "图片截取异常:X!"));
Integer y = Integer.parseInt(MyStringTools.checkParameter(request.getParameter("y"), "图片截取异常:Y!"));
Integer w = Integer.parseInt(MyStringTools.checkParameter(request.getParameter("w"), "图片截取异常:W!"));
Integer h = Integer.parseInt(MyStringTools.checkParameter(request.getParameter("h"), "图片截取异常:H!"));
String scaleWidthString = MyStringTools.checkParameter(request.getParameter("sw"), "图片截取异常:SW!");
int swIndex = scaleWidthString.indexOf("px");
Integer sw = Integer.parseInt(scaleWidthString.substring(0, swIndex));
String scaleHeightString = MyStringTools.checkParameter(request.getParameter("sh"), "图片截取异常:SH!");
int shIndex = scaleHeightString.indexOf("px");
Integer sh = Integer.parseInt(scaleHeightString.substring(0, shIndex)); //获取用户ID用于指向对应文件夹
SysUsers sysUsers = HttpTools.getSessionUser(request);
int userID = sysUsers.getUserId();
//获取文件路径
String filePath = FileTools.getPortraitPath(userID); CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(
request.getSession().getServletContext()); String path;
//检查form中是否有enctype="multipart/form-data"
if (multipartResolver.isMultipart(request)) {
//将request变成多部分request
MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
//获取multiRequest 中所有的文件名
Iterator iterator = multiRequest.getFileNames();
while (iterator.hasNext()) {
//一次遍历所有文件
MultipartFile multipartFile = multiRequest.getFile(iterator.next().toString());
if (multipartFile != null) {
String[] allowSuffix = {".jpg",".JPG"};
if (!FileTools.checkSuffix(multipartFile.getOriginalFilename(), allowSuffix)) {
throw new BusinessException("文件后缀名不符合要求!");
}
path = filePath + FileTools.getPortraitFileName(multipartFile.getOriginalFilename());
//存入硬盘
multipartFile.transferTo(new File(path));
//图片截取
if (FileTools.imgCut(path, x, y, w, h, sw, sh)) {
CompressTools compressTools = new CompressTools();
if (compressTools.simpleCompress(new File(path))) {
return JsonResult.success(FileTools.filePathToSRC(path, FileTools.IMG));
} else {
return JsonResult.error("图片压缩失败!请重新上传!");
}
} else {
return JsonResult.error("图片截取失败!请重新上传!");
}
}
}
}
return JsonResult.error("图片获取失败!请重新上传!");
}

Image图片切割


 /**
* 截图工具,根据截取的比例进行缩放裁剪
*
* @param path 图片路径
* @param zoomX 缩放后的X坐标
* @param zoomY 缩放后的Y坐标
* @param zoomW 缩放后的截取宽度
* @param zoomH 缩放后的截取高度
* @param scaleWidth 缩放后图片的宽度
* @param scaleHeight 缩放后的图片高度
* @return 是否成功
* @throws Exception 任何异常均抛出
*/
public static boolean imgCut(String path, int zoomX, int zoomY, int zoomW,
int zoomH, int scaleWidth, int scaleHeight) throws Exception {
Image img;
ImageFilter cropFilter;
BufferedImage bi = ImageIO.read(new File(path));
int fileWidth = bi.getWidth();
int fileHeight = bi.getHeight();
double scale = (double) fileWidth / (double) scaleWidth; double realX = zoomX * scale;
double realY = zoomY * scale;
double realW = zoomW * scale;
double realH = zoomH * scale; if (fileWidth >= realW && fileHeight >= realH) {
Image image = bi.getScaledInstance(fileWidth, fileHeight, Image.SCALE_DEFAULT);
cropFilter = new CropImageFilter((int) realX, (int) realY, (int) realW, (int) realH);
img = Toolkit.getDefaultToolkit().createImage(
new FilteredImageSource(image.getSource(), cropFilter));
BufferedImage bufferedImage = new BufferedImage((int) realW, (int) realH, BufferedImage.TYPE_INT_RGB);
Graphics g = bufferedImage.getGraphics();
g.drawImage(img, 0, 0, null);
g.dispose();
//输出文件
return ImageIO.write(bufferedImage, "JPEG", new File(path));
} else {
return true;
}
}

  缩放比例scale一定要用double,并且宽高也要转换成double后再相除,否则会变成求模运算,这样会降低精度,别小看这里的精度下降,最终的截图效果根据图片的缩放程度,误差可是有可能被放大的很离谱的。

图片压缩


 package com.magic.rent.tools;

 /**
* 知识产权声明:本文件自创建起,其内容的知识产权即归属于原作者,任何他人不可擅自复制或模仿.
* 创建者: wu 创建时间: 2016/12/15
* 类说明: 缩略图类(通用) 本java类能将jpg、bmp、png、gif图片文件,进行等比或非等比的大小转换。 具体使用方法
* 更新记录:
*/ import com.magic.rent.exception.custom.BusinessException;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream; public class CompressTools {
private File file; // 文件对象
private String inputDir; // 输入图路径
private String outputDir; // 输出图路径
private String inputFileName; // 输入图文件名
private String outputFileName; // 输出图文件名
private int outputWidth = 100; // 默认输出图片宽
private int outputHeight = 100; // 默认输出图片高
private boolean proportion = true; // 是否等比缩放标记(默认为等比缩放)
private static Logger logger = LoggerFactory.getLogger(CompressTools.class); public CompressTools() {
} public CompressTools(boolean proportion) {
this.proportion = proportion;
} /**
* 设置输入参数
*
* @param inputDir
* @param inputFileName
* @return
*/
public CompressTools setInputInfo(String inputDir, String inputFileName) {
this.inputDir = inputDir;
this.inputFileName = inputFileName;
return this;
} /**
* 设置输出参数
*
* @param outputDir
* @param outputFileName
* @param outputHeight
* @param outputWidth
* @param proportion
* @return
*/
public CompressTools setOutputInfo(String outputDir, String outputFileName, int outputHeight, int outputWidth, boolean proportion) {
this.outputDir = outputDir;
this.outputFileName = outputFileName;
this.outputWidth = outputWidth;
this.outputHeight = outputHeight;
this.proportion = proportion;
return this;
} // 图片处理
public boolean compress() throws Exception {
//获得源文件
file = new File(inputDir);
if (!file.exists()) {
throw new BusinessException("文件不存在!");
}
Image img = ImageIO.read(file);
// 判断图片格式是否正确
if (img.getWidth(null) == -1) {
System.out.println(" can't read,retry!" + "<BR>");
return false;
} else {
int newWidth;
int newHeight;
// 判断是否是等比缩放
if (this.proportion) {
// 为等比缩放计算输出的图片宽度及高度
double rate1 = ((double) img.getWidth(null)) / (double) outputWidth + 0.1;
double rate2 = ((double) img.getHeight(null)) / (double) outputHeight + 0.1;
// 根据缩放比率大的进行缩放控制
double rate = rate1 > rate2 ? rate1 : rate2;
newWidth = (int) (((double) img.getWidth(null)) / rate);
newHeight = (int) (((double) img.getHeight(null)) / rate);
} else {
newWidth = outputWidth; // 输出的图片宽度
newHeight = outputHeight; // 输出的图片高度
}
long start = System.currentTimeMillis();
BufferedImage tag = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
/*
* Image.SCALE_SMOOTH 的缩略算法 生成缩略图片的平滑度的
* 优先级比速度高 生成的图片质量比较好 但速度慢
*/
tag.getGraphics().drawImage(img.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH), 0, 0, null);
FileOutputStream out = new FileOutputStream(outputDir); // JPEGImageEncoder可适用于其他图片类型的转换
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
encoder.encode(tag);
out.close();
long time = System.currentTimeMillis() - start;
logger.info("[输出路径]:" + outputDir + "\t[图片名称]:" + outputFileName + "\t[压缩前大小]:" + getPicSize() + "\t[耗时]:" + time + "毫秒");
return true;
}
} /**
* 简单压缩方法,压缩后图片将直接覆盖源文件
*
* @param images
* @return
* @throws Exception
*/
public boolean simpleCompress(File images) throws Exception {
setInputInfo(images.getPath(), images.getName());
setOutputInfo(images.getPath(), images.getName(), 300, 300, true);
return compress();
} /**
* 获取图片大小,单位KB
*
* @return
*/
private String getPicSize() {
return file.length() / 1024 + "KB";
} public static void main(String[] args) throws Exception {
CompressTools compressTools = new CompressTools();
compressTools.setInputInfo("/Users/wu/Downloads/background.jpg", "background.jpg");
compressTools.setOutputInfo("/Users/wu/Downloads/background2.jpg", "background2.jpg", 633, 1920, false);
compressTools.compress();
} }

  我专门把图片压缩写成了一个类。

  其中可以看到一些关于文件路径的方法,其实没有什么特别的,就是截取后缀获取路径之类的,我这边也贴出来吧,免得有些朋友看的云里雾里的。

 package com.magic.rent.tools;

 import com.magic.rent.exception.custom.BusinessException;

 import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.CropImageFilter;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageFilter;
import java.io.File;
import java.util.ArrayList; /**
* 知识产权声明:本文件自创建起,其内容的知识产权即归属于原作者,任何他人不可擅自复制或模仿.
* 创建者: wu 创建时间: 2016/11/25
* 类说明:
* 更新记录:
*/
public class FileTools { public static final int IMG = 1; /**
* 获取项目根目录
*
* @return 根目录
*/
public static String getWebRootPath() {
return System.getProperty("web.root");
} /**
* 获取头像目录,若不存在则直接创建一个
*
* @param userID 用户ID
* @return
*/
public static String getPortraitPath(int userID) {
String realPath = getWebRootPath() + "img/portrait/" + userID + "/";
File file = new File(realPath);
//判断文件夹是否存在,不存在则创建一个
if (!file.exists() || !file.isDirectory()) {
if (!file.mkdirs()) {
throw new BusinessException("创建头像文件夹失败!");
}
}
return realPath;
} /**
* 重命名头像文件
*
* @param fileName 文件名
* @return
*/
public static String getPortraitFileName(String fileName) {
// 获取文件后缀
String suffix = getSuffix(fileName);
return "portrait" + suffix;
} /**
* 判断文件后缀是否符合要求
*
* @param fileName 文件名
* @param allowSuffix 允许的后缀集合
* @return
* @throws Exception
*/
public static boolean checkSuffix(String fileName, String[] allowSuffix) throws Exception {
String fileExtension = getSuffix(fileName);
boolean flag = false;
for (String extension : allowSuffix) {
if (fileExtension.equals(extension)) {
flag = true;
}
}
return flag;
} public static String getSuffix(String fileName) {
return fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
} /**
* 将文件地址转成链接地址
*
* @param filePath 文件路径
* @param fileType 文件类型
* @return
*/
public static String filePathToSRC(String filePath, int fileType) {
String href = "";
if (null != filePath && !filePath.equals("")) {
switch (fileType) {
case IMG:
if (filePath.contains("/img/")) {
int index = filePath.indexOf("/img/");
href = filePath.substring(index);
} else {
href = "";
}
return href;
}
}
return href;
} /**
* 获取指定文件或文件路径下的所有文件清单
*
* @param fileOrPath 文件或文件路径
* @return
*/
public static ArrayList<File> getListFiles(Object fileOrPath) {
File directory;
if (fileOrPath instanceof File) {
directory = (File) fileOrPath;
} else {
directory = new File(fileOrPath.toString());
} ArrayList<File> files = new ArrayList<File>(); if (directory.isFile()) {
files.add(directory);
return files;
} else if (directory.isDirectory()) {
File[] fileArr = directory.listFiles();
if (null != fileArr && fileArr.length != 0) {
for (File fileOne : fileArr) {
files.addAll(getListFiles(fileOne));
}
}
} return files;
} /**
* 截图工具,根据截取的比例进行缩放裁剪
*
* @param path 图片路径
* @param zoomX 缩放后的X坐标
* @param zoomY 缩放后的Y坐标
* @param zoomW 缩放后的截取宽度
* @param zoomH 缩放后的截取高度
* @param scaleWidth 缩放后图片的宽度
* @param scaleHeight 缩放后的图片高度
* @return 是否成功
* @throws Exception 任何异常均抛出
*/
public static boolean imgCut(String path, int zoomX, int zoomY, int zoomW,
int zoomH, int scaleWidth, int scaleHeight) throws Exception {
Image img;
ImageFilter cropFilter;
BufferedImage bi = ImageIO.read(new File(path));
int fileWidth = bi.getWidth();
int fileHeight = bi.getHeight();
double scale = (double) fileWidth / (double) scaleWidth; double realX = zoomX * scale;
double realY = zoomY * scale;
double realW = zoomW * scale;
double realH = zoomH * scale; if (fileWidth >= realW && fileHeight >= realH) {
Image image = bi.getScaledInstance(fileWidth, fileHeight, Image.SCALE_DEFAULT);
cropFilter = new CropImageFilter((int) realX, (int) realY, (int) realW, (int) realH);
img = Toolkit.getDefaultToolkit().createImage(
new FilteredImageSource(image.getSource(), cropFilter));
BufferedImage bufferedImage = new BufferedImage((int) realW, (int) realH, BufferedImage.TYPE_INT_RGB);
Graphics g = bufferedImage.getGraphics();
g.drawImage(img, 0, 0, null);
g.dispose();
//输出文件
return ImageIO.write(bufferedImage, "JPEG", new File(path));
} else {
return true;
}
}
}

  顺便一提:getWebRootPath这个方法,要生效,必须在Web.xml中做一个配置:

  <context-param>
<param-name>webAppRootKey</param-name>
<param-value>web.root</param-value>
</context-param>

  否则是无法动态获取项目的本地路径的。这个配置只要跟在Spring配置后面就行了,应该就不会有什么大碍,其实就是获取本地路径然后设置到系统参数当中。

  好了,这就是整个插件的功能了。

附上Github地址:


https://github.com/wuxinzhe/Portrait.git 如果有帮助,希望给个Star

[Bootstrap-插件使用]Jcrop+fileinput组合实现头像上传功能的更多相关文章

  1. 【Bootstrap-插件使用】Jcrop&plus;fileinput组合实现头像上传功能

    作者:Dreawer链接:https://zhuanlan.zhihu.com/p/24465742来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 作者:梦游的龙猫(转 ...

  2. qt实现头像上传功能

    想必大家都使用过qt的自定义头像功能吧,那么图1应该不会陌生,本片文章我就是要模拟一个这样的功能,虽然没有这么强大的效果,但是能够满足一定的需求. 图1 qq上传图片 首先在讲解功能之前,我先给出一片 ...

  3. jQuery插件ImgAreaSelect 实例讲解一(头像上传预览和裁剪功能)

    上一节随笔中,我们已经知道了关于jQuery插件ImgAreaSelect基本的知识:那么现在看一下实例: 首先,要知道我们应该实现什么功能? (1)图片能够实现上传预览功能 (2)拖拽裁剪图片,使其 ...

  4. php实现头像预览上传功能

    最近在做php第二阶段的项目,需要用到头像上传的功能 我们要完成头像上传功能,一共要写两个php页面,第一个页面我们叫做touxiang.php,第二个页面我们叫做upload.php 1.touxi ...

  5. 使用jquery插件uploadfive、jcrop实现头像上传

    1.html页面部分代码:(实现选着图片时,jcrop能够刷新图片) <script type="text/javascript"> $(function(){ $(& ...

  6. 强大的flash头像上传插件&lpar;支持旋转、拖拽、剪裁、生成缩略图等&rpar;

    今天介绍的这款flash上传头像功能非常强大,支持php,asp,jsp,asp.net 调用 头像剪裁,预览组件插件. 本组件需要安装Flash Player后才可使用,请从http://dl.pc ...

  7. 头像上传uploadPreview插件

    原文链接:https://blog.csdn.net/Alisa_L/article/details/52923953 uploadPreview 今天写头像上传,使用到uploadPreview插件 ...

  8. jquery头像上传剪裁插件cropper的前后台demo

    因为一个项目要做一个头像上传的功能,因此选择了使用jquery的头像插件cropper,cropper是一款使用简单且功能强大的图片剪裁jQuery插件,但是在使用的时候,有一个很大的坑需要注意,那就 ...

  9. java web 站点头像上传处理 &lpar;springmvc &plus;bootstrap&plus;cropper&rpar;

    制作头像上传.请依据您的实际需求.改动代码,不全然正确.仅供參考! 前端页面设计使用bootstrap ,头像预览和剪裁工具使用cropper 后台使用springmvc. 如今来看前端的页面设计 前 ...

随机推荐

  1. Core Animation一些Demo总结 (动态切换图片、大转盘、图片折叠、进度条等动画效果)

    前一篇总结了Core Animation的一些基础知识,这一篇主要是Core Animation 的一些应用,涉及到CAShapeLayer.CAReplicatorLayer等图层的知识. 先看效果 ...

  2. JS创建对象、继承原型、ES6中class继承

    面向对象编程:java中对象的两个基本概念:1.类:类是对象的模板,比如说Leader 这个是泛称领导,并不特指谁.2:实例:实例是根据类创建的对象,根据类Leader可以创建出很多实例:liyi,y ...

  3. iOS 获取设备的ip地址

    导入以下头文件 #include <ifaddrs.h> #include <arpa/inet.h>   通过下面方法即可获取ip地址+ (NSString *)getIpA ...

  4. 数据库文档生成工具——word2chm,SqlSpec

    首先使用代码生成器可以生成word版本的数据库文档. 转成chm格式的更加小巧和方便~ SqlSpec是个好工具,可以生成所有数据库相关的信息 之后可以一键生成chm文档.

  5. https&colon;&sol;&sol;www&period;chromestatus&period;com&sol;features&sol;5093566007214080

    移动端滑动报错:Unable to preventDefault inside passive event listener due to target being treated as passiv ...

  6. Arduino-接口图

    Arduino开发板上数字输入输出引脚中的3.5.6.9和11都提供0V和5V之外的可变输出.在这些引脚的旁边,会标有PWM——脉冲宽度调制,PWM是英文“Pulse Width Modulation ...

  7. react基础学习 二——生命周期

    生命周期mount: mounting装载创建 update更新 unmounting卸载 错误捕获 注意点:生命周期函数的 作用,什么之后用 只有类式组件有生命周期,函数式组件没有生命周期 moun ...

  8. 用织梦建站如何去掉a这个目录,还有内容页的a

    1.另外建一个站点,将物理路径直接指向a即可. 2.去掉文章页生成的带a的路径: 只需要这一句话:  function='str_replace("/a","" ...

  9. 对io进行分流

    package org.richin.io.Stream.util;     import java.io.BufferedInputStream;     import java.io.Buffer ...

  10. 【C&plus;&plus;】随机重命名MP3文件

    新置MP3一件,竟然没有随机播放的功能.坑啊!身为程序媛一枚,自己动手吧~ 获取当前路径: char buf[1000]; GetCurrentDirectory(1000,buf); string ...