最近用Jfinal在做Vstation,其中用到图片上传,需要实现以下功能:
1、使用webupload上传图片
2、同一个页面可以定义多个uploader,需要对webupload上传js进行封装,参数自定义
3、服务端生成缩略图
实现方法
1、自定义封装一个webupload上传js,uploadCustom.js,实现参数自定义传入和默认参数。
2、定义Jfinal中用uploadController.java,实现文件上传。
3、定义ImageKit.java工具类,需要引用com.sun.image.codec.jpeg.JPEGCodec,com.sun.image.codec.jpeg.JPEGImageEncoder,com.vstation.common.util.AnimatedGifEncoder,com.vstation.common.util.GifDecoder,实现图片的压缩。
具体代码如下:
uploadCustom.js 源码:
/**
* 基于Webupload的文件上传 js V0.1
* 实现:
* 1、同一个页面实现多个单一文件上传同时存在
* 2、自定义缩略图尺寸
* 3、自定义上传成功后文件接收对象,缩略图显示对象等
* @author James,25708164@qq.com
* @date 2016-12-7
* @since jQuery 1.9以上, Webupload 0.1.5
* @param $
*/
jQuery.uploaderCustom = function(options){
var fileCount = 0; //添加文件总数
var dOptions = { //options 默认值设置
url:'',
fileNumLimit:10,
fileSizeLimit:200 * 1024 * 1024,
fileSingleSizeLimit:50 * 1024 * 1024,
uploaderId:'Uploader',
btnId:'btn_upload',
filePicker:'filePicker',
thumb:'thumb',
thumbWidth:40,
thumbHeight:40,
filePathId:'filePath',
fileList:'fileList',
srpp:'', //swf文件路径前段,
ctx:'', //上下文context
formData:{}, //其他参数
accept: { //上传文件格式限制
title: 'Images',
extensions: 'gif,jpg,jpeg,bmp,png',
mimeTypes: 'image/jpg,image/jpeg,image/png'
},
};
if(typeof(options.url)!='undefined'){
dOptions.url = options.url;
}
if(typeof(options.fileNumLimit)!='undefined'){
dOptions.fileNumLimit = options.fileNumLimit;
}
if(typeof(options.fileSizeLimit)!='undefined'){
dOptions.fileSizeLimit = options.fileSizeLimit;
}
if(typeof(options.fileSingleSizeLimit)!='undefined'){
dOptions.fileSingleSizeLimit = options.fileSingleSizeLimit;
}
if(typeof(options.uploaderId)!='undefined'){
dOptions.uploaderId = options.uploaderId;
}
if(typeof(options.btnId)!='undefined'){
dOptions.btnId = options.btnId;
}
if(typeof(options.filePicker)!='undefined'){
dOptions.filePicker = options.filePicker;
}
if(typeof(options.thumb)!='undefined'){
dOptions.thumb = options.thumb;
}
if(typeof(options.thumbWidth)!='undefined'){
dOptions.thumbWidth = options.thumbWidth;
}
if(typeof(options.thumbHeight)!='undefined'){
dOptions.thumbHeight = options.thumbHeight;
}
if(typeof(options.filePathId)!='undefined'){
dOptions.filePathId = options.filePathId;
}
if(typeof(options.fileList)!='undefined'){
dOptions.fileList = options.fileList;
}
if(typeof(options.srpp)!='undefined'){
dOptions.srpp = options.srpp;
}
if(typeof(options.ctx)!='undefined'){
dOptions.ctx = options.ctx;
}
if(typeof(options.formData)!='undefined'){
dOptions.formData = options.formData;
}
if(typeof(options.accept)!='undefined'){
dOptions.accept = options.accept;
}
console.debug(dOptions);
var $wrap = $('#'+dOptions.uploaderId); //上传容器对象
var $uploadBtn = $('#'+dOptions.btnId); //上传按钮对象
var $filePath = $('#'+dOptions.filePathId); //上传文件路径结果接收对象
var $fileList = $('#'+dOptions.fileList); //缩略图显示对象
var $thumb = $('#'+dOptions.thumb); //缩略图值保存对象
// 优化retina, 在retina下这个值是2
var ratio = window.devicePixelRatio || 1;
// 缩略图大小
var thumbnailWidth = dOptions.thumbWidth * ratio;
var thumbnailHeight = dOptions.thumbHeight * ratio;
var state = 'pedding';
// 判断浏览器是否支持图片的base64
isSupportBase64 = ( function() {
var data = new Image();
var support = true;
data.onload = data.onerror = function() {
if( this.width != 1 || this.height != 1 ) {
support = false;
}
};
data.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
return support;
} )();
//检测是否安装了Flash插件
flashVersion = ( function() {
var version;
try {
version = navigator.plugins[ 'Shockwave Flash' ];
version = version.description;
} catch ( ex ) {
try {
version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash')
.GetVariable('$version');
} catch ( ex2 ) {
version = '0.0';
}
}
version = version.match( /\d+/g );
return parseFloat( version[ 0 ] + '.' + version[ 1 ], 10 );
} )();
//判断是否支持Flash
if ( !WebUploader.Uploader.support('flash') && WebUploader.browser.ie ) {
if (flashVersion) {
(function(container) {
window['expressinstallcallback'] = function( state ) {
switch(state) {
case "Download.Cancelled" :
alert("安装被取消");
break;
case "Download.Failed" :
alert("安装失败");
break;
default:
alert('安装已成功,请刷新!');
break;
};
delete window['expressinstallcallback'];
};
var swf = dOptions.srpp+'/plugins/webupload/expressInstall.swf';
var html = '<object type="application/x-shockwave-flash" data="' + swf + '" ';
if (WebUploader.browser.ie) {
html += 'classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" ';
}
html += 'width="100%" height="100%" style="outline:0">' +
'<param name="movie" value="' + swf + '" />' +
'<param name="wmode" value="transparent" />' +
'<param name="allowscriptaccess" value="always" />' +
'</object>';
container.html(html);
})($wrap);
// 压根就没有安转。
} else {
$wrap.html('<a href="http://www.adobe.com/go/getflashplayer" target="_blank" border="0"><img alt="get flash player" src="http://www.adobe.com/macromedia/style_guide/images/160x41_Get_Flash_Player.jpg" /></a>');
}
return;
} else if (!WebUploader.Uploader.support()) {
alert( 'Web Uploader 不支持您的浏览器!');
return;
}
var uploader;
//实例化uploader
uploader = WebUploader.create({
auto:false,
pick: '#'+dOptions.filePicker,
fileNumLimit: dOptions.fileNumLimit, //允许上传文件总数限制
fileSizeLimit: dOptions.fileSizeLimit, // 全部文件大小限制,最小单位KB
fileSingleSizeLimit: dOptions.fileSingleSizeLimit, // 单个文件大小限制,最小单位KB
swf: dOptions.srpp+'/plugins/webupload/Uploader.swf',
formData:dOptions.formData,
runtimeOrder: 'flash',
chunked: false, //true为开启分片上传
server: dOptions.url, //向服务器发送请求的路径
method:'POST',
accept: dOptions.accept, //定义可发送的文件后缀 minetype等
thumb: { //自动生成缩略图
width: thumbnailWidth,
height: thumbnailHeight,
// 图片质量,只有type为`image/jpeg`的时候才有效。
quality: 70,
// 是否允许放大,如果想要生成小图的时候不失真,此选项应该设置为false.
allowMagnify: true,
// 是否允许裁剪。
crop: true,
// 为空的话则保留原有图片格式。
// 否则强制转换成指定的类型。
type: 'image/jpeg',
},
compress:{
width: thumbnailWidth,
height: thumbnailHeight,
// 图片质量,只有type为`image/jpeg`的时候才有效。
quality: 90,
// 是否允许放大,如果想要生成小图的时候不失真,此选项应该设置为false.
allowMagnify: false,
// 是否允许裁剪。
crop: false,
// 是否保留头部meta信息。
preserveHeaders: true,
// 如果发现压缩后文件大小比原来还大,则使用原来图片
// 此属性可能会影响图片自动纠正功能
noCompressIfLarger: false,
// 单位字节,如果图片大小小于此值,不会采用压缩。
compressSize: 0
}
}).on('fileQueued', function( file ) {
/**
// 生成文件MD5信息,可用于文件在上传前后的MD5校验
this.md5File(file, 0, 1 * 1024 * 1024).progress(function(percentage) {
}) .then(function(ret) {
});
**/
}).on('uploadSuccess',function(file,response){
// console.debug(response);
// console.debug(ctx+"/"+response.filePath);
$filePath.val(dOptions.ctx+'/'+response.filePath);
$thumb.val(dOptions.ctx+'/'+response.thumbPath);
$uploadBtn.button('reset').delay(500).queue(function() {
$fileList.empty().html('<img src="'+dOptions.ctx+'/'+response.filePath+'" style="width:'+dOptions.thumbWidth+'px;height:'+dOptions.thumbHeight+'px;" class="img-circle" />');
$(this).dequeue();
});
fileCount = 0;
}).on('uploadError',function(file,response){
$uploadBtn.button('reset');
fileCount = 0;
alert('上传文件失败');
});
uploader;
//当有文件添加进来是触发生成缩略图
uploader.onFileQueued = function( file ) {
fileCount ++;
uploader.makeThumb( file, function( error, ret ) {
if ( error ) {
$fileList.text('生成缩略图失败');
} else {
$fileList.empty().html('<img src="' + ret +'" style="width:'+dOptions.thumbWidth+'px;height:'+dOptions.thumbHeight+'px;" class="img-circle" />');
$uploadBtn.button('点击上传');
}
});
};
//上传按钮click事件
$uploadBtn.on('click',function(){
if(fileCount>0){
$(this).button('loading');
uploader.upload();
}
});
};
uploadController.java 源码:
/**
文件上传Controller
@author 作者 James E-mail: 25708164@qq.com
@date 创建时间:2016年12月6日 下午2:29:42
@version 1.0
@since
@return
*/
package com.vstation.controller;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import javax.servlet.http.HttpServletRequest;
import com.alibaba.fastjson.JSONObject;
import com.jfinal.aop.Interceptor;
import com.jfinal.core.Controller;
import com.jfinal.kit.PathKit;
import com.jfinal.upload.UploadFile;
import com.vstation.common.kit.DateTimeKit;
import com.vstation.common.kit.ImageKit;
import com.vstation.test.MainConfig;
public class UploadController extends Controller {
private static int imgCount = 0;
public void imageUpload() throws IOException{
UploadFile uploadFile = getFile();
/**
* 若要获取额外参数,必须先执行上面的getFile()
*/
int thumbWidth = getParaToInt("thumbWidth",0);
int thumbHeight =getParaToInt("thumbHeight",0);
JSONObject jsonObject = new JSONObject();
String fileName = "";
if (uploadFile != null) {
fileName = uploadFile.getFileName();
String extentionName = fileName.substring(fileName
.lastIndexOf(".")); // 后缀名
if (imgCount > 300)// 300为文件上传最大数目
imgCount = 0;
String newName = DateTimeKit.getCurrentTime() + "_" + imgCount
+ extentionName;// 新名
imgCount++;
// 文件完整路径
String filePath = PathKit.getWebRootPath()+"/"+MainConfig.absolutePath + "/" + newName;
String thumbPath = PathKit.getWebRootPath()+"/"+MainConfig.absolutePath + "/thumb_" + newName;
// 重命名并上传文件
uploadFile.getFile().renameTo(new File(filePath));
//生成缩略图
if(thumbWidth!=0 && thumbHeight!=0){
ImageKit.scaleImage(thumbWidth, thumbWidth, filePath, thumbPath);
}
//返回任意数据即代表上传成功
jsonObject.put("returnCode","success");
jsonObject.put("filePath", MainConfig.absolutePath + "/" + newName);
jsonObject.put("thumbPath", MainConfig.absolutePath + "/thumb_" + newName);
}else {
//返回任意数据即代表上传成功
jsonObject.put("returnCode","fail");
}
renderJson(jsonObject.toJSONString());
}
}
ImageKit.java 源码
package com.vstation.common.kit;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.imageio.ImageIO;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import com.vstation.common.util.AnimatedGifEncoder;
import com.vstation.common.util.GifDecoder;
public class ImageKit {
/**
* 根据给定高和宽和目录生成图片(包括GIF)
* @param width
* @param height
* @param inPath
* @param outPath
* @throws IOException
*/
public static void zoomImage(int width,int height,String inPath,String outPath) throws IOException{
File infile = new File(inPath);
BufferedImage buffimg = ImageIO.read(infile);
double dwidth = buffimg.getWidth();
double dheight = buffimg.getHeight();
// if (dwidth != width && dheight != height) {
if (inPath.toLowerCase().endsWith(".gif")) {
String temp = outPath.substring(0,outPath.lastIndexOf("/")+1);
String[] fList = splitGif(inPath,temp);
int[] delay = new int[fList.length];
for (int i = 0; i < fList.length; i++) {
scaleImage(width, height, fList[i], fList[i]);
delay[i] = Integer.parseInt(fList[i].substring(fList[i]
.lastIndexOf("_") + 1, fList[i].lastIndexOf(".")));
}
jpgToGif(fList,delay,outPath);
for(int i = 0;i<fList.length;i++){
File f = new File(fList[i]);
f.deleteOnExit();
}
} else {
scaleImage(width, height, inPath, outPath);
}
// }
}
/**
* 根据给定高和宽和目录生成图片
* @param width
* @param height
* @param inPath
* @param outPath
* @throws IOException
*/
public static void scaleImage(int width,int height,String inPath,String outPath) throws IOException{
File infile = new File(inPath);
BufferedImage buffimg = ImageIO.read(infile);
double dwidth = buffimg.getWidth();
double dheight = buffimg.getHeight();
// System.out.println("width = "+dwidth);
// System.out.println("height = "+dheight);
// if(dwidth!=width&&dheight!=height){
try{
double ratew = width / dwidth;
double rateh = height / dheight;
// 获得适合的缩放比率,即以在规定缩略尺寸中完整显示图片内容的同时又保证最大的缩放比率
double rate = Math.min(ratew, rateh);
rate = (Math.rint((rate * 100) + 0.5)) / 100;
BufferedImage imgmini = new BufferedImage(width,
height, BufferedImage.TYPE_USHORT_565_RGB);
Graphics2D gmini = imgmini.createGraphics();
gmini.setBackground(Color.WHITE);
gmini.clearRect(0, 0, width, height);
AffineTransform trans = new AffineTransform();
trans.scale(rate, rate);
AffineTransformOp op = new AffineTransformOp(trans,
AffineTransformOp.TYPE_BILINEAR);
gmini.drawImage(buffimg, op, (int) (width - (dwidth * rate)) / 2,
(int) (height - (dheight * rate)) / 2);
ImageIO.write(imgmini, "jpg", new File(outPath));
}catch(Exception ex){
ex.printStackTrace();
}
// }
}
/**
* 把多张jpg图片合成一张
* @param pic String[] 多个jpg文件名 包含路径
* @param newPic String 生成的gif文件名 包含路径
*/
private static void jpgToGif(String pic[],int[] delay, String newGif) {
try {
AnimatedGifEncoder e = new AnimatedGifEncoder(); //网上可以找到此类
e.setRepeat(0);
e.start(newGif);
BufferedImage src[] = new BufferedImage[pic.length];
for (int i = 0; i < src.length; i++) {
e.setDelay(delay[i]); //设置播放的延迟时间
src[i] = ImageIO.read(new File(pic[i])); // 读入需要播放的jpg文件
e.addFrame(src[i]); //添加到帧中
}
e.finish();
} catch (Exception e) {
System.err.println( "jpgToGif Failed:");
e.printStackTrace();
}
}
/**
* 把gif图片按帧拆分成jpg图片
* @param gifName String 小gif图片(路径+名称)
* @param path String 生成小jpg图片的路径
* @return String[] 返回生成小jpg图片的名称
*/
private static String[] splitGif(String gifName,String path) {
FileOutputStream out = null;
try {
GifDecoder decoder = new GifDecoder();
decoder.read(new BufferedInputStream(new DataInputStream(new FileInputStream(new File(gifName)))));
int n = decoder.getFrameCount(); //得到frame的个数
String[] subPic = new String[n];
for (int i = 0; i < n; i++) {
BufferedImage frame = decoder.getFrame(i); // 得到帧
int delay = decoder.getDelay(i); // 得到延迟时间
// 生成小的JPG文件
subPic[i] = path + i +"_"+ delay+".jpg";
out = new FileOutputStream(subPic[i]);
ImageIO.write(frame, "gif", out);
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
encoder.encode(frame); // 存盘
out.flush();
out.close();
}
return subPic;
} catch (Exception e) {
// System.out.println( "splitGif Failed!");
e.printStackTrace();
return null;
}finally{
try{
if(out!=null)out.close();
}catch(Exception ex){
}
}
}
public static void main(String[] args) {
String inPath = "D:/MyProgram/WorkSpace/vstation/WebRoot/uploadFile/images/20161214/20161214164822_32.png";
String outPath = "D:/test/2.png";
try{
ImageKit.zoomImage(80,80,inPath,outPath);
// ImageHelper.scaleImage(80, 80, inPath, outPath);
}catch(Exception ex){
ex.printStackTrace();
}
}
}
html页面调用代码:
<div id="Uploader" class="form-group">
<label class="col-sm-3 control-label">缩略图</label>
<div class="col-sm-5">
<span id="fileList" style="vertical-align:50%;margin-left:20px;" >最佳尺寸:80x80</span>
<span id="filePicker" class="form-inline" style="margin-left:20px;" >点击选择</span>
<input type="hidden" name="filePath" id="filePath"/> <!-- 上传成功后,接收回传新文件名 -->
<input type="hidden" name="thumb" id="thumb" />
<span style="vertical-align:50%;">
<button id="btn_upload" type="button" class="btn btn-default form-inline" data-loading-text="正在上传...">上传</button>
</span>
</div>
</div>
<script>
//---------- 初始化上传组件 begin--------------//
var uploadOptions ={
url:'${ctx}/upload/imageUpload', //Jfinal中uploadController的Action路径
fileNumLimit:10,
fileSizeLimit:20 * 1024 * 1024,
fileSingleSizeLimit:5 * 1024 * 1024,
thumbWidth:80,
thumbHeight:80,
srpp:'${srpp}', //swf文件路径前段,
ctx:'${ctx}', //上下文context
formData:{
thumbWidth:80, //服务端生成缩略图width
thumbHeight:80, //服务端生成缩略图height
}
};
$.uploaderCustom(uploadOptions);
//---------- 初始化上传组件 end --------------//
</script>