WebUploader+SpringMVC实现文件上传功能

WebUploader是由Baidu团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件。在现代的浏览器里面能充分发挥html5的优势,同时又不摒弃主流IE浏览器,沿用原来的FLASH运行时,兼容IE6+,iOS 6+, Android 4+。两套运行时,同样的调用方式,可供用户任意选用。 采用大文件分片并发上传,极大的提高了文件上传效率。

官方文档及更多示例请参考: http://fex.baidu.com/webuploader/

不扯废话了,由于我需要的只是上传图片功能,官网上边还说“WebUploader只包含文件上传的底层实现,不包括UI部分,所以交互方面可以自由发挥。”但是我又看到官网的例子不错,就把demo的js和css扒了下来.

前台相关代码:

jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8"
 pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<!--引入CSS-->
<link rel="stylesheet" type="text/css" href="${basePath}/js/plugins/webuploader/webuploader.css" rel="external nofollow" >
<link rel="stylesheet" type="text/css" href="${basePath}/views/uploaderDemo/uploader_demo.css" rel="external nofollow" >
<!--引入JS-->
<script src="${basePath}/js/jquery.min.js"></script>
<script src="${basePath}/js/plugins/webuploader/webuploader.js"></script>
<script src="${basePath}/js/bootstrap.min.js"></script>
<script src="${basePath}/views/uploaderDemo/uploader_demo.js"></script>
<title>uploadTest</title>
</head>
<body>
 <div id="post-container" class="container">
  <div class="page-container">
   <h1 id="demo">Demo</h1>
   <p>您可以尝试文件拖拽,使用QQ截屏工具,然后激活窗口后粘贴,或者点击添加图片按钮,来体验此demo.</p>
   <div id="uploader" class="wu-example">
    <div class="queueList">
     <div id="dndArea" class="placeholder">
      <div id="filePicker"></div>
      <p>或将照片拖到这里,单次最多可选300张</p>
     </div>
    </div>
    <div class="statusBar" style="display: none;">
     <div class="progress">
      <span class="text">0%</span> <span class="percentage"></span>
     </div>
     <div class="info"></div>
     <div class="btns">
      <div id="filePicker2"></div>
      <div class="uploadBtn">开始上传</div>
     </div>
    </div>
   </div>
  </div>
 </div>
</body>
</html>

uploader_demo.js

jQuery(function() {
 var $ = jQuery, // just in case. Make sure it's not an other libaray.
  $wrap = $('#uploader'),  // 图片容器
  $queue = $('<ul class="filelist"></ul>')
   .appendTo( $wrap.find('.queueList') ),  // 状态栏,包括进度和控制按钮
  $statusBa $wrap.find('.statusBar'),
  // 文件总体选择信息。
  $info = $statusBar.find('.info'),
  // 上传按钮
  $upload = $wrap.find('.uploadBtn'),
  // 没选择文件之前的内容。
  $placeHolder = $wrap.find('.placeholder'),
  // 总体进度条
  $progress = $statusBar.find('.progress').hide(),
  // 添加的文件数量
  fileCount = 0,
  // 添加的文件总大小
  fileSize = 0,
  // 优化retina, 在retina下这个值是2
  ratio = window.devicePixelRatio || 1,
  // 缩略图大小
  thumbnailWidth = 110 * ratio,
  thumbnailHeight = 110 * ratio,
  // 可能有pedding, ready, uploading, confirm, done.
  state = 'pedding',
  // 所有文件的进度信息,key为file id
  percentages = {},
  supportTransition = (function(){
   var s = document.createElement('p').style,
    r = 'transition' in s ||
      'WebkitTransition' in s ||
      'MozTransition' in s ||
      'msTransition' in s ||
      'OTransition' in s;
   s = null;
   return r;
  })(),
  // WebUploader实例
  uploader;
 if ( !WebUploader.Uploader.support() ) {
  alert( 'Web Uploader 不支持您的浏览器!如果你使用的是IE浏览器,请尝试升级 flash 播放器');
  throw new Error( 'WebUploader does not support the browser you are using.' );
 }
 // 实例化
 uploader = WebUploader.create({
  pick: {
   id: '#filePicker',
   label: '点击选择图片'
  },
  dnd: '#uploader .queueList',
  paste: document.body,
  accept: {
   title: 'Images',
   extensions: 'gif,jpg,jpeg,bmp,png',
   mimeTypes: 'image/*'
  },
  // swf文件路径,就是控件下的swf文件地址
  swf: 'js/plugins/webuploader/Uploader.swf',
  disableGlobalDnd: true,
  chunked: true,
  // 文件接收服务端,写你要执行的方法就行
  **server: 'http://localhost:8080/test/upload.do?method=upload'**,
  fileNumLimit: 300,
  fileSizeLimit: 5 * 1024 * 1024, // 200 M
  fileSingleSizeLimit: 1 * 1024 * 1024 // 50 M
 });
 // 添加“添加文件”的按钮,
 uploader.addButton({
  id: '#filePicker2',
  label: '继续添加'
 });
 // 当有文件添加进来时执行,负责view的创建
 function addFile( file ) {
  var $li = $( '<li id="' + file.id + '">' +
    '<p class="title">' + file.name + '</p>' +
    '<p class="imgWrap"></p>'+
    '<p class="progress"><span></span></p>' +
    '</li>' ),
   $btns = $('<div class="file-panel">' +
    '<span class="cancel">删除</span>' +
    '<span class="rotateRight">向右旋转</span>' +
    '<span class="rotateLeft">向左旋转</span></div>').appendTo( $li ),
   $prgress = $li.find('p.progress span'),
   $wrap = $li.find( 'p.imgWrap' ),
   $info = $('<p class="error"></p>'),
   showError = function( code ) {
    switch( code ) {
     case 'exceed_size':
      text = '文件大小超出';
      break;
     case 'interrupt':
      text = '上传暂停';
      break;
     default:
      text = '上传失败,请重试';
      break;
    }
    $info.text( text ).appendTo( $li );
   };
  if ( file.getStatus() === 'invalid' ) {
   showError( file.statusText );
  } else {
   // @todo lazyload
   $wrap.text( '预览中' );
   uploader.makeThumb( file, function( error, src ) {
    if ( error ) {
     $wrap.text( '不能预览' );
     return;
    }
    var img = $('<img src="'+src+'">');
    $wrap.empty().append( img );
   }, thumbnailWidth, thumbnailHeight );
   percentages[ file.id ] = [ file.size, 0 ];
   file.rotation = 0;
  }
  file.on('statuschange', function( cur, prev ) {
   if ( prev === 'progress' ) {
    $prgress.hide().width(0);
   } else if ( prev === 'queued' ) {
    $li.off( 'mouseenter mouseleave' );
    $btns.remove();
   }
   // 成功
   if ( cur === 'error' || cur === 'invalid' ) {
    console.log( file.statusText );
    showError( file.statusText );
    percentages[ file.id ][ 1 ] = 1;
   } else if ( cur === 'interrupt' ) {
    showError( 'interrupt' );
   } else if ( cur === 'queued' ) {
    percentages[ file.id ][ 1 ] = 0;
   } else if ( cur === 'progress' ) {
    $info.remove();
    $prgress.css('display', 'block');
   } else if ( cur === 'complete' ) {
    $li.append( '<span class="success"></span>' );
   }
   $li.removeClass( 'state-' + prev ).addClass( 'state-' + cur );
  });
  $li.on( 'mouseenter', function() {
   $btns.stop().animate({height: 30});
  });
  $li.on( 'mouseleave', function() {
   $btns.stop().animate({height: 0});
  });
  $btns.on( 'click', 'span', function() {
   var index = $(this).index(),
    deg;
   switch ( index ) {
    case 0:
     uploader.removeFile( file );
     return;
    case 1:
     file.rotation += 90;
     break;
    case 2:
     file.rotation -= 90;
     break;
   }
   if ( supportTransition ) {
    deg = 'rotate(' + file.rotation + 'deg)';
    $wrap.css({
     '-webkit-transform': deg,
     '-mos-transform': deg,
     '-o-transform': deg,
     'transform': deg
    });
   } else {
    $wrap.css( 'filter', 'progid:DXImageTransform.Microsoft.BasicImage(rotation='+ (~~((file.rotation/90)%4 + 4)%4) +')');
    // use jquery animate to rotation
    // $({
    //  rotation: rotation
    // }).animate({
    //  rotation: file.rotation
    // }, {
    //  easing: 'linear',
    //  step: function( now ) {
    //   now = now * Math.PI / 180;
    //   var cos = Math.cos( now ),
    //    sin = Math.sin( now );
    //   $wrap.css( 'filter', "progid:DXImageTransform.Microsoft.Matrix(M11=" + cos + ",M12=" + (-sin) + ",M21=" + sin + ",M22=" + cos + ",SizingMethod='auto expand')");
    //  }
    // });
   }
  });
  $li.appendTo( $queue );
 }
 // 负责view的销毁
 function removeFile( file ) {
  var $li = $('#'+file.id);
  delete percentages[ file.id ];
  updateTotalProgress();
  $li.off().find('.file-panel').off().end().remove();
 }
 function updateTotalProgress() {
  var loaded = 0,
   total = 0,
   spans = $progress.children(),
   percent;
  $.each( percentages, function( k, v ) {
   total += v[ 0 ];
   loaded += v[ 0 ] * v[ 1 ];
  } );
  percent = total ? loaded / total : 0;
  spans.eq( 0 ).text( Math.round( percent * 100 ) + '%' );
  spans.eq( 1 ).css( 'width', Math.round( percent * 100 ) + '%' );
  updateStatus();
 }
 function updateStatus() {
  var text = '', stats;
  if ( state === 'ready' ) {
   text = '选中' + fileCount + '张图片,共' +
     WebUploader.formatSize( fileSize ) + '。';
  } else if ( state === 'confirm' ) {
   stats = uploader.getStats();
   if ( stats.uploadFailNum ) {
    text = '已成功上传' + stats.successNum+ '张照片至XX相册,'+
     stats.uploadFailNum + '张照片上传失败,<a class="retry" href="#" rel="external nofollow" rel="external nofollow" >重新上传</a>失败图片或<a class="ignore" href="#" rel="external nofollow" rel="external nofollow" >忽略</a>'
   }
  } else {
   stats = uploader.getStats();
   text = '共' + fileCount + '张(' +
     WebUploader.formatSize( fileSize ) +
     '),已上传' + stats.successNum + '张';
   if ( stats.uploadFailNum ) {
    text += ',失败' + stats.uploadFailNum + '张';
   }
  }
  $info.html( text );
 }
 function setState( val ) {
  var file, stats;
  if ( val === state ) {
   return;
  }
  $upload.removeClass( 'state-' + state );
  $upload.addClass( 'state-' + val );
  state = val;
  switch ( state ) {
   case 'pedding':
    $placeHolder.removeClass( 'element-invisible' );
    $queue.parent().removeClass('filled');
    $queue.hide();
    $statusBar.addClass( 'element-invisible' );
    uploader.refresh();
    break;
   case 'ready':
    $placeHolder.addClass( 'element-invisible' );
    $( '#filePicker2' ).removeClass( 'element-invisible');
    $queue.parent().addClass('filled');
    $queue.show();
    $statusBar.removeClass('element-invisible');
    uploader.refresh();
    break;
   case 'uploading':
    $( '#filePicker2' ).addClass( 'element-invisible' );
    $progress.show();
    $upload.text( '暂停上传' );
    break;
   case 'paused':
    $progress.show();
    $upload.text( '继续上传' );
    break;
   case 'confirm':
    $progress.hide();
    $upload.text( '开始上传' ).addClass( 'disabled' );
    stats = uploader.getStats();
    if ( stats.successNum && !stats.uploadFailNum ) {
     setState( 'finish' );
     return;
    }
    break;
   case 'finish':
    stats = uploader.getStats();
    if ( stats.successNum ) {
     alert( '上传成功' );
    } else {
     // 没有成功的图片,重设
     state = 'done';
     location.reload();
    }
    break;
  }
  updateStatus();
 }
 uploader.onUploadProgress = function( file, percentage ) {
  var $li = $('#'+file.id),
   $percent = $li.find('.progress span');
  $percent.css( 'width', percentage * 100 + '%' );
  percentages[ file.id ][ 1 ] = percentage;
  updateTotalProgress();
 };
 uploader.onFileQueued = function( file ) {
  fileCount++;
  fileSize += file.size;
  if ( fileCount === 1 ) {
   $placeHolder.addClass( 'element-invisible' );
   $statusBar.show();
  }
  addFile( file );
  setState( 'ready' );
  updateTotalProgress();
 };
 uploader.onFileDequeued = function( file ) {
  fileCount--;
  fileSize -= file.size;
  if ( !fileCount ) {
   setState( 'pedding' );
  }
  removeFile( file );
  updateTotalProgress();
 };
 uploader.on( 'all', function( type ) {
  var stats;
  switch( type ) {
   case 'uploadFinished':
    setState( 'confirm' );
    break;
   case 'startUpload':
    setState( 'uploading' );
    break;
   case 'stopUpload':
    setState( 'paused' );
    break;
  }
 });
 uploader.onError = function( code ) {
  alert( 'Eroor: ' + code );
 };
 $upload.on('click', function() {
  if ( $(this).hasClass( 'disabled' ) ) {
   return false;
  }
  if ( state === 'ready' ) {
   uploader.upload();
  } else if ( state === 'paused' ) {
   uploader.upload();
  } else if ( state === 'uploading' ) {
   uploader.stop();
  }
 });
 $info.on( 'click', '.retry', function() {
  uploader.retry();
 } );
 $info.on( 'click', '.ignore', function() {
  alert( 'todo' );
 } );
 $upload.addClass( 'state-' + state );
 updateTotalProgres});

uploader_demo.css

#container {
 color: #838383;
 font-size: 12px;
}
#uploader .queueList {
 margin: 20px;
 border: 3px dashed #e6e6e6;
}
#uploader .queueList.filled {
 padding: 17px;
 margin: 0;
 border: 3px dashed transparent;
}
#uploader .queueList.webuploader-dnd-over {
 border: 3px dashed #999999;
}
#uploader p {margin: 0;}
.element-invisible {
 position: absolute !important;
 clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
 clip: rect(1px,1px,1px,1px);
}
#uploader .placeholder {
 min-height: 350px;
 padding-top: 178px;
 text-align: center;
 background: url(../images/image.png) center 93px no-repeat;
 color: #cccccc;
 font-size: 18px;
 position: relative;
}
#uploader .placeholder .webuploader-pick {
 font-size: 18px;
 background: #00b7ee;
 border-radius: 3px;
 line-height: 44px;
 padding: 0 30px;
 *width: 120px;
 color: #fff;
 display: inline-block;
 margin: 0 auto 20px auto;
 cursor: pointer;
 box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
}
#uploader .placeholder .webuploader-pick-hover {
 background: #00a2d4;
}
#uploader .placeholder .flashTip {
 color: #666666;
 font-size: 12px;
 position: absolute;
 width: 100%;
 text-align: center;
 bottom: 20px;
}
#uploader .placeholder .flashTip a {
 color: #0785d1;
 text-decoration: none;
}
#uploader .placeholder .flashTip a:hover {
 text-decoration: underline;
}
#uploader .filelist {
 list-style: none;
 margin: 0;
 padding: 0;
}
#uploader .filelist:after {
 content: '';
 display: block;
 width: 0;
 height: 0;
 overflow: hidden;
 clear: both;
}
#uploader .filelist li {
 width: 110px;
 height: 110px;
 background: url(../images/bg.png) no-repeat;
 text-align: center;
 margin: 0 8px 20px 0;
 position: relative;
 display: inline;
 float: left;
 overflow: hidden;
 font-size: 12px;
}
#uploader .filelist li p.log {
 position: relative;
 top: -45px;
}
#uploader .filelist li p.title {
 position: absolute;
 top: 0;
 left: 0;
 width: 100%;
 overflow: hidden;
 white-space: nowrap;
 text-overflow : ellipsis;
 top: 5px;
 text-indent: 5px;
 text-align: left;
}
#uploader .filelist li p.progress {
 position: absolute;
 width: 100%;
 bottom: 0;
 left: 0;
 height: 8px;
 overflow: hidden;
 z-index: 50;
 margin: 0;
 border-radius: 0;
 background: none;
 -webkit-box-shadow: 0 0 0;
}
#uploader .filelist li p.progress span {
 display: none;
 overflow: hidden;
 width: 0;
 height: 100%;
 background: #1483d8 url(../images/progress.png) repeat-x;
 -webit-transition: width 200ms linear;
 -moz-transition: width 200ms linear;
 -o-transition: width 200ms linear;
 -ms-transition: width 200ms linear;
 transition: width 200ms linear;
 -webkit-animation: progressmove 2s linear infinite;
 -moz-animation: progressmove 2s linear infinite;
 -o-animation: progressmove 2s linear infinite;
 -ms-animation: progressmove 2s linear infinite;
 animation: progressmove 2s linear infinite;
 -webkit-transform: translateZ(0);
}
@-webkit-keyframes progressmove {
 0% {
  background-position: 0 0;
 }
 100% {
  background-position: 17px 0;
 }
}
@-moz-keyframes progressmove {
 0% {
  background-position: 0 0;
 }
 100% {
  background-position: 17px 0;
 }
}
@keyframes progressmove {
 0% {
  background-position: 0 0;
 }
 100% {
  background-position: 17px 0;
 }
}
#uploader .filelist li p.imgWrap {
 position: relative;
 z-index: 2;
 line-height: 110px;
 vertical-align: middle;
 overflow: hidden;
 width: 110px;
 height: 110px;
 -webkit-transform-origin: 50% 50%;
 -moz-transform-origin: 50% 50%;
 -o-transform-origin: 50% 50%;
 -ms-transform-origin: 50% 50%;
 transform-origin: 50% 50%;
 -webit-transition: 200ms ease-out;
 -moz-transition: 200ms ease-out;
 -o-transition: 200ms ease-out;
 -ms-transition: 200ms ease-out;
 transition: 200ms ease-out;
}
#uploader .filelist li img {
 width: 100%;
}
#uploader .filelist li p.error {
 background: #f43838;
 color: #fff;
 position: absolute;
 bottom: 0;
 left: 0;
 height: 28px;
 line-height: 28px;
 width: 100%;
 z-index: 100;
}
#uploader .filelist li .success {
 display: block;
 position: absolute;
 left: 0;
 bottom: 0;
 height: 40px;
 width: 100%;
 z-index: 200;
 background: url(../images/success.png) no-repeat right bottom;
}
#uploader .filelist div.file-panel {
 position: absolute;
 height: 0;
 filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#80000000', endColorstr='#80000000')\0;
 background: rgba( 0, 0, 0, 0.5 );
 width: 100%;
 top: 0;
 left: 0;
 overflow: hidden;
 z-index: 300;
}
#uploader .filelist div.file-panel span {
 width: 24px;
 height: 24px;
 display: inline;
 float: right;
 text-indent: -9999px;
 overflow: hidden;
 background: url(../../img/uploader_icons.png) no-repeat;
 margin: 5px 1px 1px;
 cursor: pointer; 
}
#uploader .filelist div.file-panel span.rotateLeft {
 background-position: 0 -24px;
}
#uploader .filelist div.file-panel span.rotateLeft:hover {
 background-position: 0 0;
}
#uploader .filelist div.file-panel span.rotateRight {
 background-position: -24px -24px;
}
#uploader .filelist div.file-panel span.rotateRight:hover {
 background-position: -24px 0;
}
#uploader .filelist div.file-panel span.cancel {
 background-position: -48px -24px;
}
#uploader .filelist div.file-panel span.cancel:hover {
 background-position: -48px 0;
}
#uploader .statusBar {
 height: 63px;
 border-top: 1px solid #dadada;
 padding: 0 20px;
 line-height: 63px;
 vertical-align: middle;
 position: relative;
}
#uploader .statusBar .progress {
 border: 1px solid #1483d8;
 width: 198px;
 background: #fff;
 height: 18px;
 position: relative;
 display: inline-block;
 text-align: center;
 line-height: 20px;
 color: #6dbfff;
 position: relative;
 margin: 0 10px 0 0;
}
#uploader .statusBar .progress span.percentage {
 width: 0;
 height: 100%;
 left: 0;
 top: 0;
 background: #1483d8;
 position: absolute;
}
#uploader .statusBar .progress span.text {
 position: relative;
 z-index: 10;
}
#uploader .statusBar .info {
 display: inline-block;
 font-size: 14px;
 color: #666666;
}
#uploader .statusBar .btns {
 position: absolute;
 top: 10px;
 right: 20px;
 line-height: 40px;
}
#filePicker2 {
 display: inline-block;
 float: left;
}
#uploader .statusBar .btns .webuploader-pick,
#uploader .statusBar .btns .uploadBtn,
#uploader .statusBar .btns .uploadBtn.state-uploading,
#uploader .statusBar .btns .uploadBtn.state-paused {
 background: #ffffff;
 border: 1px solid #cfcfcf;
 color: #565656;
 padding: 0 18px;
 display: inline-block;
 border-radius: 3px;
 margin-left: 10px;
 cursor: pointer;
 font-size: 14px;
 float: left;
}
#uploader .statusBar .btns .webuploader-pick-hover,
#uploader .statusBar .btns .uploadBtn:hover,
#uploader .statusBar .btns .uploadBtn.state-uploading:hover,
#uploader .statusBar .btns .uploadBtn.state-paused:hover {
 background: #f0f0f0;
}
#uploader .statusBar .btns .uploadBtn {
 background: #00b7ee;
 color: #fff;
 border-color: transparent;
}
#uploader .statusBar .btns .uploadBtn:hover {
 background: #00a2d4;
}
#uploader .statusBar .btns .uploadBtn.disabled {
 pointer-events: none;
 opacity: 0.6;
}

后台代码:

@Controller
@RequestMapping("/upload")
public class UpLoaderTestController {
 @RequestMapping(params = "method=uploadPic")
 public String uploadPic(HttpServletRequest request){
  return "uploaderDemo/uploaderTest";
 }
 @RequestMapping(params="method=upload")
 @ResponseBody
 public String uploads(@RequestParam("file")MultipartFile sortPicImg,HttpServletRequest request,HttpServletResponse response) {
  String path = SysConstants.PIC_SERVER_FILE_ROOT_DIR +SysConstants.PIC_PTYPE_DIR ;
  String fileName = System.currentTimeMillis()+"_"+sortPicImg.getOriginalFilename();
  File targetFile = new File(path, fileName);
  if (!targetFile.exists()) {
   targetFile.mkdirs();
  }
  JSONObject json = new JSONObject();
  //保存
  try {
   sortPicImg.transferTo(targetFile);
  } catch (Exception e) {
   e.printStackTrace();
   json.put("msg","error");
   return json.toJSONString();
  }
  json.put("msg","success");
  //json.put("filePath",request.getContextPath() + "/upload/" + fileName);
  File retfile = new File(SysConstants.PIC_SERVER_DNS +SysConstants.PIC_PTYPE_DIR, fileName);
  json.put("filePath",retfile.getPath());
  System.out.println("json="+json.toJSONString());
  return json.toJSONString();
 }
 }

前台传到后台的图片是一张一张传的,每传一张图片调用一次该server方法,直到传完为止。

将图片文件上传到图片服务器后,再将该图片地址存储在数据库中。

声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:notice#nhooo.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。