先看下我们实际项目中的最终效果完成效果图:
下面详细说明一下,这个里面主要用到的技术
图片上传选用的jquery.uploadify插件
截图插件采用的是jquery.imageaeraselect-0.9.10
图片中第一行的红色字体首先提示允许上传的图片的格式和允许上传的图片的最大尺寸,这个做开发的筒子们应该都清楚。
为什么要限制分辨率在600*300以上呢?这个是因为下面的截图框的大小为600*300。本来我也想做随着图片自适应大小的,但是后来考虑到页面的大小就作死了,感觉这样比较方便。
阐述一下截图的原理,个人研究出来的,有问题请发邮件给我:zhuzj40721p@126.com
web截图的原理主要如下,首先鼠标点击某一个点,这个时候就产生出了一个起始点的坐标,然后截图插件会随着我们的鼠标不断移动产生出截图区域的宽度和高度,等我们松开鼠标的时候就会产生一个终点的坐标,这个时候截图就完成了。筒子们可能会问右边的预览是怎么实时出来的,其实这个也简单的,是我们通过css样式计算出来的,稍后我会一一解答。
左下方的图片建议大小是我们项目中所需要,筒子们可以根据自己的需要自己设置。
jquery.imageareaselect下载地址:http://odyniec.net/projects/imgareaselect/
其实这个网站上有很多东西,大家可以去参考一下。
jquery.uploadify 大家可以自行百度。我用的是jquery.uploadifive。其实差不多,大家可以自行百度,因为这个是收费的插件我就不放下载地址,我只能把相关代码贴出来。
下面是主要的代码
核心HTML代码:
<div class="container">
<div class="formControls col-xs-12 col-sm-12 uploadDiv">
<input id="file_upload" name="file" type="file" multiple="false">
<font style="color:red">(支持JPG,JPEG,PNG,GIF,TIFF等格式图片,最大2M,分辨率:600*300以上)</font>
<input type="hidden" value="" placeholder="" id="pic" name="pic">
<div id="queue"></div>
<div style="display:none;" class="picDiv">
<img id="pic_upload" width="80px;" height="60px;" class="picture-thumb" src=""/>
</div>
</div>
<div>
<table>
<tr>
<td style="width:65%;">
<div style="margin-top: 10px;padding-left:20px;">
<p class="instructions"></p>
<!--
http://bftu-wechat-circle.oss-cn-beijing.aliyuncs.com/resources/demo.png
-->
<div class="frame" style="margin: 0 0.3em;" style="width: 600px; height: 300px;">
<img id="photo" src="http://bftu-wechat-circle.oss-cn-beijing.aliyuncs.com/resources/test.png" style="width: 600px; height: 300px;" alt="截图图片">
</div>
</div>
<div style="float:left; width:40%;padding-left:20px;clear:both;">
<div class="picDiv">
<label>截图坐标:</label><br/>
<table class="picDiv">
<tr>
<td style="width: 10px;">x1:</td>
<td style="width: 60px;">
<input type="text" class="input-text" id="x1" name="x1" value=""/>
</td>
<td style="width: 10px;padding-left:30px;">y1:</td>
<td style="width: 60px;">
<input type="text" class="input-text" id="y1" name="y1" value=""/>
</td>
<td style="width: 10px;padding-left:30px;">x2:</td>
<td style="width: 60px;">
<input type="text" class="input-text" id="x2" name="x2" value=""/>
</td>
<td style="width: 10px;padding-left:30px;">y2:</td>
<td style="width: 60px;">
<input type="text" class="input-text" id="y2" name="y2" value=""/>
</td>
</tr>
</table>
</div>
</div>
<div style="float:left; width:40%;padding-left:20px;clear:both;">
<div class="picDiv">
<label>图片大小:</label><br/>
<table class="picDiv">
<tr>
<td style="width: 10px;">宽:</td>
<td style="width: 60px;">
<input type="text" class="input-text" id="width" name="width" value=""/>
</td>
<td style="width: 10px;padding-left:30px;">高:</td>
<td style="width: 60px;">
<input type="text" class="input-text" id="height" name="height" value=""/>
</td>
<td style="width: 60px;padding-left:30px;"><font style="color:red">建议宽高:</font></td>
<td style="width: 30px;">
<font style="color:red">375*175</font>
</td>
</tr>
</table>
</div>
</div>
</td>
<td style="width:35%;clear:both;">
<div style="margin-top:-200px;">
<p class="instructions"><font size="3;">截图预览:</font></p>
<div id="preview" class="frame" style="margin: 0 0.3em; width: 300px; height: 150px;overflow:hidden;">
<!--
http://bftu-wechat-circle.oss-cn-beijing.aliyuncs.com/resources/demo.png
-->
<img src="http://bftu-wechat-circle.oss-cn-beijing.aliyuncs.com/resources/test.png" style="width: 300px; height: 150px;" alt="缩略图片">
</div>
</div>
<div class="cl">
<div style="margin-top:50px;">
<input class="btn btn-primary radius" type="submit" value=" 确定 " id="submitBtn">
</div>
</div>
</td>
</tr>
</table>
</div>
</div>
==========================================================
核心css代码
.check-box {
box-sizing: border-box;
cursor: pointer;
display: inline;
position: relative;
padding-left: 30px;
padding-right: 20px;
}
.divClass {
min-height: 50px;
max-height: 300px;
overflow: auto;
}
.picDiv {
margin-top: 10px;
}
.uploadDiv{
margin-top:30px;
}
.leftDiv{
padding-top:30px;
}
=========================================
核心js代码
$(function () {
$("#file_upload").uploadifive({
'fileObjName': 'file',
'auto': true,
'buttonClass': 'btn btn-primary',
'buttonText': "选择文件",
'height': '35px',
'queueID': 'queue',
"fileType": "*.jpg;*.gif;*.png;*.jpeg;", //允许上传文件类型
'fileSizeLimit': '5MB',
'removeCompleted': true, //上传完成后自动隐藏列表
'multi': false,
'uploadScript': CTX_PATH + '/upload/type/image',
'onError': function(errorType) {
if(errorType === "FILE_SIZE_LIMIT_EXCEEDED"){
layer.alert("上传的图片大小不能超过2M");
}
// alert('The error was: ' + errorType);
},
'onUploadComplete': function (file, data) {
var result = JSON.parse(data);
//显示图片框
if(result.result == 0){
layer.alert(result.msg);
}else{
$("#photo").attr('src', result.extra);
$("#preview img").attr('src', result.extra);
}
}
});
//剪裁图片的核心方法
//图片剪切区域处理
$('#photo').imgAreaSelect({
x1:0,
y1:0,
x2:100,
y2:100,
aspectRatio: '2.15:1', //比例
handles: true,
fadeSpeed:200,
onSelectChange: function(img, selection){//图片剪切区域变化时触发
$("#x1").val(selection.x1);
$("#y1").val(selection.y1);
$("#x2").val(selection.x2);
$("#y2").val(selection.y2);
//显示出宽和高
$("#width").val(selection.x2-selection.x1);
$("#height").val(selection.y2-selection.y1);
//预览出图片显示
var scaleX = 300 / selection.width;
var scaleY = 150 / selection.height;
$('#preview img').css({
width: Math.round(scaleX * 600),
height: Math.round(scaleY * 300),
marginLeft: -Math.round(scaleX * selection.x1),
marginTop: -Math.round(scaleY * selection.y1)
});
},
onSelectEnd: function (img, selection) {//图片剪切区域结束时触发
$('input[name="x1"]').val(selection.x1);
$('input[name="y1"]').val(selection.y1);
$('input[name="x2"]').val(selection.x2);
$('input[name="y2"]').val(selection.y2);
//显示出宽和高
$("#width").val(selection.x2 - selection.x1);
$("#height").val(selection.y2 - selection.y1);
}
});
//提交截图的方法
$("#submitBtn").click(function(){
var x1 = $("input[name='x1']").val();
var y1 = $("input[name='y1']").val();
var x2 = $("input[name='x2']").val();
var y2 = $("input[name='y2']").val();
var img64 = $("#photo").attr("src");
var url = CTX_PATH+"/upload/copper";
var param = {
'x1': x1,
'y1': y1,
'x2': x2,
'y2': y2,
'image': img64,
'type':1, //参数类型 轮播图 1 菜单2 活动3 资讯4
}
//提交到后台处理图片
$.post(url,param,function(data){
var result = JSON.parse(data);
if(result.result == 200){
var index = parent.layer.getFrameIndex(window.name); //获取当前窗体索引
parent.layer.close(index); //执行关闭
//对添加页面进行赋值操作
parent.$("#pic").val(result.obj);
//显示图片框
parent.$('.picDiv').css("display", "block");
parent.$("#pic_upload").attr('src', result.extra);
}
});
})
});
======================================
核心后台代码
第一步上传图片的代码,并进行校验
JsonResult jsonResult = new JsonResult();
Properties properties = PropertiesUtil.getInstance().load("file");
String filePath = properties.getProperty("filePath");
Properties property = PropertiesUtil.getInstance().load("image");
String imageWidth = property.getProperty("image_width");
String imageHeight = property.getProperty("image_height");
String newFileName = null;
try {
File dir = new File(filePath);
if(!dir.exists()){
dir.mkdirs();
}
String fileExt = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1).toLowerCase();
if(!imgTypes.contains(fileExt)){
jsonResult.setMsg("只允许上传.jpg、jpeg、gif、png、bmp格式");
jsonResult.setResult(0);
}else{
//如果是图片的话还需要校验图片的大小,默认大小就是400*300
newFileName = CommonUtil.getUuid()+"." + fileExt;
File newFile = new File(filePath +File.separator + newFileName);
file.transferTo(newFile);
int width = ImageUtil.getImgWidth(newFile);
int height = ImageUtil.getImgHeight(newFile);
if(width >= Integer.valueOf(imageWidth) && height >= Integer.valueOf(imageHeight)){
//调用阿里云oss服务,存储上去
OSSClient ossClient = AliyunOSSClientUtil.getOSSClient();
String backet_name = properties.getProperty("aliBucket");
String folder = properties.getProperty("alifolder");
String pic = AliyunOSSClientUtil.uploadObject2OSS(ossClient, newFile, backet_name, folder);
//调用七牛云的服务,存储上去
//String pic = QiniuUtil.upload(newFileName, newFile.getPath());
if(StringUtils.isNotBlank(pic)){
jsonResult.setMsg("上传图片成功");
jsonResult.setObj(newFileName);
jsonResult.setExtra(pic);
jsonResult.setResult(HttpStatusCode.Ok);
//上传成功之后把图片删除掉
newFile.delete();
}
}else{
jsonResult.setMsg("图片宽高必须为600*300以上");
jsonResult.setResult(0);
//上传失败之后把图片也删除掉
newFile.delete();
}
}
} catch (Exception e) {
logger.error(e.getMessage());
jsonResult.setResult(HttpStatusCode.Internal_Server_Error);
jsonResult.setMsg("上传图片失败");
}
return jsonResult;
==============================================
第二步 点击确定按钮,实现裁剪的代码
JsonResult jsonResult = new JsonResult();
String x1 = request.getParameter("x1");
String y1 = request.getParameter("y1");
String x2 = request.getParameter("x2");
String y2 = request.getParameter("y2");
String url = request.getParameter("image");
String type = request.getParameter("type");
Properties properties = PropertiesUtil.getInstance().load("file");
Properties property = PropertiesUtil.getInstance().load("image");
String filePath = properties.getProperty("filePath");
String imageWidth = property.getProperty("image_width");
String imageHeight = property.getProperty("image_height");
//下载文件到指定目录
String name = url.substring(url.lastIndexOf("/")+1);
String suffix = url.substring(url.lastIndexOf(".")+1);
HttpUtil.post2File(url, filePath);
//把图片缩减到指定的尺寸
File file = new File(filePath+File.separator+name);
String newName = CommonUtil.generate32UUID()+"."+suffix;
ImageUtil.scaleImage(suffix, file.getAbsolutePath(), filePath+File.separator+newName, Integer.valueOf(imageWidth), Integer.valueOf(imageHeight));
//对图片进行裁剪
String uuid = CommonUtil.generate32UUID();
String sourcePath = filePath+File.separator+newName;
String targetPath = filePath+File.separator+uuid+"."+suffix;
ImageUtil.cutImage(suffix, sourcePath, targetPath, Integer.valueOf(x1), Integer.valueOf(y1), Integer.valueOf(x2)-Integer.valueOf(x1), Integer.valueOf(y2)-Integer.valueOf(y1));
//将截图的图片上传到阿里云oss上去并把原来的图片删除掉
//调用阿里云oss服务,存储上去
OSSClient ossClient = AliyunOSSClientUtil.getOSSClient();
String backet_name = properties.getProperty("aliBucket");
String folder = properties.getProperty("alifolder");
String pic = AliyunOSSClientUtil.uploadObject2OSS(ossClient, new File(targetPath), backet_name, folder);
if(StringUtils.isNotBlank(pic)){
jsonResult.setMsg("上传图片成功");
jsonResult.setObj(uuid+"."+suffix);
jsonResult.setExtra(pic);
jsonResult.setResult(HttpStatusCode.Ok);
//上传成功之后把图片删除掉
file.delete();
new File(sourcePath).delete();
new File(targetPath).delete();
}
return jsonResult;
}
=====================================================
imageUtil核心工具类
private static final Logger logger = LoggerFactory.getLogger(ImageUtil.class);
/**
* 旋转图像。
* @param bufferedImage 图像。
* @param degree 旋转角度。
* @return 旋转后的图像。
*/
public static BufferedImage rotateImage(
final BufferedImage bufferedImage, final int degree) {
int width = bufferedImage.getWidth();
int height = bufferedImage.getHeight();
int type = bufferedImage.getColorModel().getTransparency();
BufferedImage image = new BufferedImage(width, height, type);
Graphics2D graphics2D = image.createGraphics();
graphics2D.setRenderingHint(
RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
graphics2D.rotate(Math.toRadians(degree), width / 2, height / 2);
graphics2D.drawImage(bufferedImage, 0, 0, null);
try {
return image;
} finally {
if (graphics2D != null) {
graphics2D.dispose();
}
}
}
/**
* 将图像按照指定的比例缩放。
* 比如需要将图像放大20%,那么调用时scale参数的值就为20;如果是缩小,则scale值为-20。
* @param bufferedImage 图像。
* @param scale 缩放比例。
* @return 缩放后的图像。
*/
public static BufferedImage resizeImageScale(
final BufferedImage bufferedImage, final int scale) {
if (scale == 0) {
return bufferedImage;
}
int width = bufferedImage.getWidth();
int height = bufferedImage.getHeight();
int newWidth = 0;
int newHeight = 0;
double nowScale = (double) Math.abs(scale) / 100;
if (scale > 0) {
newWidth = (int) (width * (1 + nowScale));
newHeight = (int) (height * (1 + nowScale));
} else if (scale < 0) {
newWidth = (int) (width * (1 - nowScale));
newHeight = (int) (height * (1 - nowScale));
}
return resizeImage(bufferedImage, newWidth, newHeight);
}
/**
* 将图像缩放到指定的宽高大小。
* @param bufferedImage 图像。
* @param width 新的宽度。
* @param height 新的高度。
* @return 缩放后的图像。
*/
public static BufferedImage resizeImage(
final BufferedImage bufferedImage,
final int width, final int height) {
int type = bufferedImage.getColorModel().getTransparency();
BufferedImage image = new BufferedImage(width, height, type);
Graphics2D graphics2D = image.createGraphics();
graphics2D.setRenderingHint(
RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
graphics2D.drawImage(bufferedImage, 0, 0, width, height, 0, 0,
bufferedImage.getWidth(), bufferedImage.getHeight(), null);
try {
return image;
} finally {
if (graphics2D != null) {
graphics2D.dispose();
}
}
}
/**
* 将图像水平翻转。
* @param bufferedImage 图像。
* @return 翻转后的图像。
*/
public static BufferedImage flipImage(
final BufferedImage bufferedImage) {
int width = bufferedImage.getWidth();
int height = bufferedImage.getHeight();
int type = bufferedImage.getColorModel().getTransparency();
BufferedImage image = new BufferedImage(width, height, type);
Graphics2D graphics2D = image.createGraphics();
graphics2D.drawImage(bufferedImage, 0, 0, width, height,
width, 0, 0, height, null);
try {
return image;
} finally {
if (graphics2D != null) {
graphics2D.dispose();
}
}
}
/**
* 获取图片的类型。如果是 gif、jpg、png、bmp 以外的类型则返回null。
* @param imageBytes 图片字节数组。
* @return 图片类型。
* @throws java.io.IOException IO异常。
*/
public static String getImageType(final byte[] imageBytes)
throws IOException {
ByteArrayInputStream input = new ByteArrayInputStream(imageBytes);
ImageInputStream imageInput = ImageIO.createImageInputStream(input);
Iterator<ImageReader> iterator = ImageIO.getImageReaders(imageInput);
String type = null;
if (iterator.hasNext()) {
ImageReader reader = iterator.next();
type = reader.getFormatName().toUpperCase();
}
try {
return type;
} finally {
if (imageInput != null) {
imageInput.close();
}
}
}
/**
* 验证图片大小是否超出指定的尺寸。未超出指定大小返回true,超出指定大小则返回false。
* @param imageBytes 图片字节数组。
* @param width 图片宽度。
* @param height 图片高度。
* @return 验证结果。未超出指定大小返回true,超出指定大小则返回false。
* @throws java.io.IOException IO异常。
*/
public static boolean checkImageSize(
final byte[] imageBytes, final int width, final int height)
throws IOException {
BufferedImage image = byteToImage(imageBytes);
int sourceWidth = image.getWidth();
int sourceHeight = image.getHeight();
if (sourceWidth > width || sourceHeight > height) {
return false;
} else {
return true;
}
}
/**
* 将图像字节数组转化为BufferedImage图像实例。
* @param imageBytes 图像字节数组。
* @return BufferedImage图像实例。
* @throws java.io.IOException IO异常。
*/
public static BufferedImage byteToImage(
final byte[] imageBytes) throws IOException {
ByteArrayInputStream input = new ByteArrayInputStream(imageBytes);
BufferedImage image = ImageIO.read(input);
try {
return image;
} finally {
if (input != null) {
input.close();
}
}
}
/**
* 将BufferedImage持有的图像转化为指定图像格式的字节数组。
* @param bufferedImage 图像。
* @param formatName 图像格式名称。
* @return 指定图像格式的字节数组。
* @throws java.io.IOException IO异常。
*/
public static byte[] imageToByte(
final BufferedImage bufferedImage, final String formatName)
throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, formatName, output);
try {
return output.toByteArray();
} finally {
if (output != null) {
output.close();
}
}
}
/**
* 将图像以指定的格式进行输出,调用者需自行关闭输出流。
* @param bufferedImage 图像。
* @param output 输出流。
* @param formatName 图片格式名称。
* @throws java.io.IOException IO异常。
*/
public static void writeImage(final BufferedImage bufferedImage,
final OutputStream output, final String formatName)
throws IOException {
ImageIO.write(bufferedImage, formatName, output);
}
/**
* 将图像以指定的格式进行输出,调用者需自行关闭输出流。
* @param imageBytes 图像的byte数组。
* @param output 输出流。
* @param formatName 图片格式名称。
* @throws java.io.IOException IO异常。
*/
public static void writeImage(final byte[] imageBytes,
final OutputStream output, final String formatName)
throws IOException {
BufferedImage image = byteToImage(imageBytes);
ImageIO.write(image, formatName, output);
}
/**
* 按照指定的区域截图图片
* @param suffix
* @param sourcePath
* @param targetPath
* @param x1
* @param y1
* @param x2
* @param y2
*/
/*public static void cutImage(String suffix, String sourcePath, String targetPath,
int x1, int y1, int x2, int y2) {
Image img = null;
ImageFilter cropFilter = null;
try {
File sourceImgFile = new File(sourcePath);
BufferedImage bi = ImageIO.read(sourceImgFile);
int srcWidth = bi.getWidth();
int srcHeight = bi.getHeight();
int destWidth = x2 - x1;
int destHeight = y2 - y1;
if (srcWidth >= destWidth && srcHeight >= destHeight) {
Image image = bi.getScaledInstance(srcWidth, srcHeight,
Image.SCALE_DEFAULT);
cropFilter = new CropImageFilter(x1, y1, destWidth, destHeight);
img = Toolkit.getDefaultToolkit().createImage(
new FilteredImageSource(image.getSource(), cropFilter));
BufferedImage tag = new BufferedImage(destWidth, destHeight,
BufferedImage.TYPE_INT_RGB);
Graphics g = tag.getGraphics();
g.drawImage(img, 0, 0, null);
g.dispose();
ImageIO.write(tag, suffix, new File(targetPath));
}
} catch (Exception e) {
logger.error(e.getMessage());
}
}*/
public static void scaleImage(String suffix,String srcPath,String destPath,int imageWidth,int imageHeight){
BufferedImage image = null;
try {
image = ImageIO.read(new FileInputStream(srcPath));
image = ImageUtil.resizeImage(image,imageWidth,imageHeight);
ImageIO.write(image, suffix, new File(destPath));
} catch (Exception e) {
logger.error(e.getMessage());
}
}
public static void cutImage(String suffix,String srcpath,String subpath,int x,int y,int width,int height){
FileInputStream is = null;
ImageInputStream iis = null;
try {
// 读取图片文件
is = new FileInputStream(srcpath);
Iterator<ImageReader> it = ImageIO.getImageReadersByFormatName(suffix);
ImageReader reader = it.next();
// 获取图片流
iis = ImageIO.createImageInputStream(is);
reader.setInput(iis, true);
ImageReadParam param = reader.getDefaultReadParam();
// 图片裁剪区域。Rectangle 指定了坐标空间中的一个区域,通过 Rectangle 对象的左上顶点的坐标(x,y)、宽度和高度可以定义这个区域。
Rectangle rect = new Rectangle(x, y, width, height);
// 提供一个 BufferedImage,将其用作解码像素数据的目标。
param.setSourceRegion(rect);
BufferedImage bi = reader.read(0, param);
// 保存新图片
ImageIO.write(bi, suffix, new File(subpath));
}catch(Exception e){
logger.error(e.getMessage());
}finally {
if(is != null){
try {
is.close();
} catch (IOException e) {
logger.error(e.getMessage());
}
}
if(iis != null){
try {
iis.close();
} catch (IOException e) {
logger.error(e.getMessage());
}
}
}
}
/**
* 获取图片宽度
* @param file 图片文件
* @return 宽度
*/
public static int getImgWidth(File file) {
InputStream is = null;
BufferedImage src = null;
int ret = -1;
try {
is = new FileInputStream(file);
src = javax.imageio.ImageIO.read(is);
ret = src.getWidth(null); // 得到源图宽
is.close();
} catch (Exception e) {
logger.error(e.getMessage());
}
return ret;
}
/**
* 获取图片高度
* @param file 图片文件
* @return 高度
*/
public static int getImgHeight(File file) {
InputStream is = null;
BufferedImage src = null;
int ret = -1;
try {
is = new FileInputStream(file);
src = javax.imageio.ImageIO.read(is);
ret = src.getHeight(null); // 得到源图高
is.close();
} catch (Exception e) {
logger.error(e.getMessage());
}
return ret;
}
======================================================
到这里已经把所有的代码都实现了,到这里筒子们可能会问,我在后台是怎么实现截图和坐标定位的呢
首先我的项目中是把图片上传到阿里云上,然后我通过http接口下载图片到本地,首先我得把图片压缩到600*300,这个是为什么呢?jquery.imageareaselect.js这个插件,在定位坐标的时候是根据前端页面的截图框的大小来定位的,所以我们在后台的时候需要把图片压缩到600*300。之后再根据传入进来的x1 y1 x2 y2来算出截图的大小。并进行截图的操作。这样操作的话截图就能顺利完成啦!