基于SpringMVC文件上传服务器端进度条实现

基于SpringMVC文件上传服务器端进度条实现

    <div class="article_manage clearfix">
    <div class="article_l">
        <span class="link_categories">
        标签:
          <a href="http://www.csdn.net/tag/%e6%9c%8d%e5%8a%a1%e5%99%a8" target="_blank" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_tag']);">服务器</a><a href="http://www.csdn.net/tag/%e5%88%86%e5%b8%83%e5%bc%8f%e5%ad%98%e5%82%a8" target="_blank" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_tag']);">分布式存储</a><a href="http://www.csdn.net/tag/buffer" target="_blank" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_tag']);">buffer</a><a href="http://www.csdn.net/tag/%e6%89%a9%e5%b1%95" target="_blank" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_tag']);">扩展</a><a href="http://www.csdn.net/tag/io" target="_blank" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_tag']);">io</a><a href="http://www.csdn.net/tag/flash" target="_blank" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_tag']);">flash</a>
        </span>
    </div>
    <div class="article_r">
        <span class="link_postdate">2012-04-09 13:02</span>
        <span class="link_view" title="阅读次数">7727人阅读</span>
        <span class="link_comments" title="评论次数"> <a href="#comments" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_pinglun'])">评论</a>(4)</span>
        <span class="link_collect tracking-ad" data-mod="popu_171"> <a href="javascript:void(0);" onclick="javascript:collectArticle('基于SpringMVC文件上传服务器端进度条实现','7440483');return false;" title="收藏" target="_blank">收藏</a></span>
         <span class="link_report"> <a href="#report" onclick="javascript:report(7440483,2);return false;" title="举报">举报</a></span>

    </div>
</div>
<div class="embody" style="display:none" id="embody">
    <span class="embody_t">本文章已收录于:</span>
    <div class="embody_c" id="lib" value="{&quot;err&quot;:0,&quot;msg&quot;:&quot;ok&quot;,&quot;data&quot;:[]}"></div>
</div>
<style type="text/css">        
        .embody{
            padding:10px 10px 10px;
            margin:0 -20px;
            border-bottom:solid 1px #ededed;                
        }
        .embody_b{
            margin:0 ;
            padding:10px 0;
        }
        .embody .embody_t,.embody .embody_c{
            display: inline-block;
            margin-right:10px;
        }
        .embody_t{
            font-size: 12px;
            color:#999;
        }
        .embody_c{
            font-size: 12px;
        }
        .embody_c img,.embody_c em{
            display: inline-block;
            vertical-align: middle;               
        }
         .embody_c img{               
            width:30px;
            height:30px;
        }
        .embody_c em{
            margin: 0 20px 0 10px;
            color:#333;
            font-style: normal;
        }
</style>
<script type="text/javascript">
    $(function () {
        var lib = eval("("+$("#lib").attr("value")+")");
        var html = "";
        if (lib.err == 0) {
            $.each(lib.data, function (i) {
                var obj = lib.data[i];
                //html += '<img src="' + obj.logo + '"/>' + obj.name + "&nbsp;&nbsp;";
                html += ' <a href="' + obj.url + '" target="_blank">';
                html += ' <img src="' + obj.logo + '">';
                html += ' <em><b>' + obj.name + '</b></em>';
                html += ' </a>';
            });
            if (html != "") {
                setTimeout(function () {
                    $("#lib").html(html);
                    /*
                    var testhtml = "";
                    testhtml += '<a href="#">';
                    testhtml += '<img src="http://img.knowledge.csdn.net/upload/base/1455589744328_328.jpg">';
                    testhtml += '<em>Android知识库</em>';
                    testhtml += '</a>';
                    $(".embody_c").html(testhtml);
                    */
                    $("#embody").show();
                }, 100);
            }
        }          

    });
</script>
<script type="text/javascript" src="http://static.blog.csdn.net/scripts/category.js"></script>  
文件上传应该大部分人都接触过,一般都是基于commons-fileupload组件来实现,SpringMVC的文件上传功能也是在commons-fileupload组件提供的功能上面做了一些包装功能,使文件上传开发更容易方便。
        首先来看看我们系统对于文件上传功能提出来的需求:
        1、能同时上传多个文件;
        2、单个文件大小不超过2G;
        3、要有进度条可以实时显示上传进度;
        4、可以取消正在上传的文件;
        5、文件上传到分布式存储系统,保证上传的效率。

        一、实现普通文件上传
        我们知道,类DispatcherServlet是SpringMVC的入口,在其doDispatch方法里面,我们可以看到它会先去检查有没配置multipartResolver  

        如果有的话会先执行它的resolveMultipart方法:

        因此,我们在配置文件中添加:
                则可使用SpringMVC自带的CommonsMultipartResolver实现多个文件上传的功能。

        二、实现进度条
        现在实现带进度条的文件上传一般都是在客户端用flash上传组件计算,或者是通过客户端插件的方式。前者上传组件有大小的限制,后者对于我们系统来说实现过于复杂,开发难度稍大。因此我们采用服务器端计算文件上传进度,客户端轮询的方式。
        Commons-fileupload组件自带了文件上传进度的监听器,类FileUploadBase提供了它的set方法:        
        
        ProgressListener是一个接口,我们需要自己实现它的update方法,参数pBytesRead表示已经上传到服务器的字节数,pContentLength表示所有文件的总大小,pItems表示第几个文件:
        
        SpringMVC没有实现监听器,所以如果要监听的话得自己扩展CommonsMultipartResolver类,在newFileUpload里面加入代码设置自己实现的监听器:

        ServletFileUploadExt是对ServletFileUpload类进行的扩展,后面会提到。这样在文件上传的过程中,监听器将得到通知已上传的字节数:
        
        


        三、上传到分布式存储系统
        通过阅读commons-fileupload的源码我们知道实际文件上传发生在FileUploadBase类parseRequest(RequestContext ctx)方法里面:
         

        因此我们可以通过继承ServletFileUpload(该类继承自FileUpload)类重写parseRequest方法来实现我们自己的存储方式。这里有个地方需要注意的,一般文件保存到分布式存储系统中,为了减少IO次数,需要实现本地的buffer,等buffer满了后再上传上去:
        
         但是因为commons-fileupload组件本身有个buffer,而且buffer大小为4096:
         
        
        因此按照上面实现的话不管自己定义多大的buffer,一次IO传上去的实际不到4096字节(减去http头信息等),我们需要用扩展了BufferedInputStream的类包装下:
        
        BufferedInputStreamExt主要对read方法做了一些修改:
        
        去掉了BufferedInputStream类该方法的几条语句,因为在上传组件自带buffer的情况下,读一次后input.available()会为0,此处会导致提前返回而不能把自定义的buffer读满:
        

        四、取消文件上传
        取消文件上传实现方式为上传时保存上传输入流的引用,取消时关闭流,让输入流产生IO异常或者数组越界异常,同时捕获这些异常,则可取消文件上传。

        五、总结&改进
        由于SpringMVC上传的时候并不能得到单个文件的大小,配置文件限制的大小也是所有文件的大小,非单个文件大小,监听的也是整体文件上传的进度。所以在客户端可以做一些改进,比如模拟一次可以上传多个文件,但实际上传时只上传单个文件,等第一个文件上传完毕后再依次发请求上传第二第三个文件。

document.getElementById("bdshell_js").src = "http://bdimg.share.baidu.com/static/js/shell_v2.js?cdnversion=" + Math.ceil(new Date()/3600000)
    <div id="digg" articleid="7440483">
        <dl id="btnDigg" class="digg digg_enable" onclick="btndigga();">

             <dt>顶</dt>
            <dd>0</dd>
        </dl>


        <dl id="btnBury" class="digg digg_enable" onclick="btnburya();">

              <dt>踩</dt>
            <dd>1</dd>               
        </dl>

    </div>
 <div class="tracking-ad" data-mod="popu_222"><a href="javascript:void(0);" target="_blank">&nbsp;</a>   </div>
<div class="tracking-ad" data-mod="popu_223"> <a href="javascript:void(0);" target="_blank">&nbsp;</a></div>
<script type="text/javascript">
            function btndigga() {
                $(".tracking-ad[data-mod='popu_222'] a").click();
            }
            function btnburya() {
                $(".tracking-ad[data-mod='popu_223'] a").click();
            }
        </script>

<div style="clear:both; height:10px;"></div>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值