文件上传 multipart

允许用户利用multipart请求将本地文件上传到服务器,是Spring MVC其中的一个优势。它提供了Multipart一系列api方便了文件的上传
Spring通过对Servlet API的HttpServletRequest接口进行扩展,使其能够很好地处理文件上传。扩展后的接口名为org.springframework.web.multipart.MultipartHttpServletRequest

首先 前端提交的表单中如果包含文件 ,需要采用post 方式 ,enctype要设置为multipart/form-data ,这种方式不对字符进行编码,支持文件上传。


Spring 文件上传API spring.framework.web.multipart

multipart包结构

<p> </p>

multipart包常用类与接口

SpringMVC 文件上传过程:

1. 配置MultipartResolver,该接口有两个实现,如图。(也可以通过在controller中配置MultipartResolver进行实现!http://blog.csdn.net/awangz/article/details/9471161)

可通过defaultEncoding设置编码方式;通过maxUploadSize设置大小限制;可通过uploadTempDir设置存储路径。

2.写表单 注意enctype=multipart/form-data.
3.写文件上传控制类

关键代码:new MultipartFile 先new该对象 在调用其 .transferTo(new File(path目标存储路径))
关键点:可用RequestParam绑定参数MultipartFile [] 到控制器方法参数;也可HttpServletRequest获取
特点:相比输入输出流,传输的更快

坑:DispacherServlet中已经固定了MultipartResolver的id=multipartResolver不可修改

源码分析 原理解析

  • Spring multipart继承于Apache common upload,其代码实现都是调用Apache的文件上传类与方法实现的,multipart上传其内部是调用apache的上传组件DeferredFileOutputStream实现的。
  • DeferredFileOutputStream又封装着MultipartStream流,MultipartStream提供对客户端传来的multipart文件类型数据的处理方法。
  • 客户端提供的multipart文件是分区块的,一个请求中若enctype采用multipart类型MIME编码,则传输的文件不对字符编码,传输的数据按readBoundary()取得的分界符进行分隔,每个区段对应表单的一个input,其中有content-type字段的区段是文件数据,服务器端调用MultipartStream的readHeader()、readBodyData()方法,获取字段中数据。再看MultipartStream其中源码,其实也就是用输入输出流实现的。

demo:

xml
  <bean id="multipartResolver"  
    class="org.springframework.web.multipart.commons.CommonsMultipartResolver" 
    p:defaultEncoding="utf-8" />
  </beans>
controller

    /*lyy 文件上传  学习 multipart 
     * 2016-11-4
     * 
     * */
    @RequestMapping(params = "doAdd2",method = RequestMethod.POST)
    public ModelAndView doAddFiles(BookInfoEntity bookInfo, HttpServletRequest request, @RequestParam("files") MultipartFile[] files) throws ParseException {   
        
        if (StringUtil.isNotEmpty(bookInfo.getId())) {
            BookInfoEntity t = bookInfoService.get(BookInfoEntity.class, bookInfo.getId());
            try {
                MyBeanUtils.copyBeanNotNull2Bean(bookInfo, t);
                bookInfoService.saveOrUpdate(t);
            } catch (Exception e) {
                e.printStackTrace();
            
            }
        } else {
            if(bookInfo.getBookImage()==null){
                bookInfo.setBookImage("images/book_images/book1.JPG");
            }
            String bookName="";
            try {
                //bookName = new String(bookInfo.getBookName().getBytes("ISO-8859-1"), "UTF-8");
                bookName = java.net.URLDecoder.decode(bookInfo.getBookName(), "UTF-8");
            } catch (UnsupportedEncodingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
            bookInfo.setBookName(bookName);
            bookInfo.setAuditStatus(1);
            bookInfo.setBookAuditor("8a4eb0c8569b66f401569b8412900004");
            bookInfo.setCreateBy("8a4eb0c8569b66f401569b8412900004");
            bookInfo.setIsenable(1);
            Date currentTime = new Date();
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = formatter.format(currentTime);  
            //图片上传


            
            //多文件上传
            //判断file数组不能为空且长度大于0
            if(files!=null&&files.length>0){
                //循环获取file数组中文件
                for(int i=0;i<files.length;i++){
                    MultipartFile file=files[i];
                    boolean tmp=saveFile(file);
                }
            }
            
            bookInfo.setCreateTime(dateString); 
            bookInfoService.save(bookInfo);
        }
        return goAuditList(request);
    }
    
    //文件保存 用于文件上传   lyy
    private boolean saveFile(MultipartFile file) {
        String realPath="D:\\";
        String newFileName;
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
        newFileName =df.format(new Date()) + "_" + new Random().nextInt(1000) + file.getOriginalFilename();                 
        String savePath = realPath +newFileName;// 文件保存全路径
        try {
            file.transferTo(new File(savePath));//文件保存
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
        
        
    }
jsp
 <div class="box box-color box-bordered">
 <div id="add_book" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="width:80%; ">
        <nav class="modal-header box-title " style ="margin-top:0px">
        <button type="button" class="close" data-dismiss="modal" aria-hidden="true" style="color: white; margin-right: 10px;">×</button>
            <h3>
                <i class="icon-table"></i>
                新建图书
            </h3>
        </nav>
        <div class="box-content nopadding">
        <form id="add_book_form" action="bookInfoController.do?doAdd2" method="POST" enctype="multipart/form-data"  class="form-horizontal form-bordered  box-color" >
        <div class="container-fluid">
            <div class="row-fluid">
                <div class="span6">
                    <div class="control-group">
                        <label for="textfield" class="control-label">图书名称:</label>
                        <div class="controls" >
                            <input type="text" name="bookName" placeholder="图书名称" class="input-block-level" data-rule-required="true" data-rule-rangelength="[1,50]" data-msg-rangelength="名称不超过50字符">
                        </div>
                    </div>
                </div>
                <div class="span6">
                    <div class="control-group">
                        <label for="textfield" class="control-label">图书作者:</label>
                        <div class="controls">
                            <input type="text" name="bookAuthor" placeholder="图书作者"class="input-block-level" data-rule-required="true" data-rule-rangelength="[1,50]" data-msg-rangelength="作者名不超过50字符">
                        </div>
                    </div>
                </div>
                
            </div>
            <div class="row-fluid">
                <div class="span6">
                    <div class="control-group">
                        <label for="textfield" class="control-label">图书分类:</label>
                        <div class="controls">
                            <select id="bookClass" name="bookClass"  class="input-block-level" datatype="*" data-rule-required="true">
                                <option value="">*请选择书籍分类</option>
                                <c:forEach items="${bookClassList}" var="item">
                                <c:choose>
                                <c:when test="${item.id==bookClass}">  
                                      <option value="${item.id }" selected="selected">${item.className}</option>     
                                    </c:when>
                                    <c:otherwise>
                                      <option value="${item.id }">${item.className}</option>
                                    </c:otherwise>
                                </c:choose>
                                </c:forEach>
                            </select>
                        </div>
                    </div>
                </div>
                <div class="span6">
                    <div class="control-group">
                        <label for="textfield" class="control-label">图书拥有者:</label>
                        <div class="controls">
                            <select id="bookOwner" name="bookOwner"  class="input-block-level" datatype="*" data-rule-required="true" data-rule-rangelength="[1,50]" data-msg-rangelength="拥有者姓名不超过50字符">
                                <option value="">*请选择拥有者</option>
                                <c:forEach items="${userList}" var="item">
                                <c:choose>
                                <c:when test="${item.id==book_owner}">  
                                      <option value="${item.id }" selected="selected">${item.userName}</option>     
                                    </c:when>
                                    <c:otherwise>
                                      <option value="${item.id }">${item.userName}</option>
                                    </c:otherwise>
                                </c:choose>
                                </c:forEach>
                            </select>
                        </div>
                    </div>
                </div>
            </div>
            <div class="row-fluid">
                <div class="span6">
                    <div class="control-group">
                        <label for="tasktitel" class="control-label">图书ISBN:</label>
                        <div class="controls">
                            <input type="text" name="bookIsbn" placeholder="ISBN" class="input-block-level" data-rule-required="true" data-rule-rangelength="[1,13]" data-msg-rangelength="ISBN长度为10位或13位" >
                        </div>
                    </div>
                </div>
                <div class="span6">
                    <div class="control-group">
                        <label for="tasktitel" class="control-label">出版社:</label>
                        <div class="controls">
                            <input type="text" name="bookPress" placeholder="出版社"class="input-block-level" data-rule-required="true" data-rule-rangelength="[1,50]" data-msg-rangelength="出版社名称长度不超过50字符">
                        </div>
                    </div>
                </div>
            </div>  
            <div class="row-fluid">
                <div class="span6">
                    <div class="control-group">
                        <label for="tasktitel" class="control-label">出版时间:</label>
                        <div class="controls">
                            <input type="text" name="bookPressTime" id="textfield" placeholder="出版时间"  class="input-block-level datepick" data-date-format="yyyy-mm-dd">
                        </div>
                    </div>
                </div>  
            </div>
            <div class="row-fluid">
                <div class="span12">
                    <div class="control-group bor-top">
                        <label for="textfield" class="control-label" style=" width:100px;">摘要:</label>
                        <div class="controls">
                            <textarea name="bookAbstract" id="bookAbstract" rows="3" class="input-block-level" data-rule-required="true" data-rule-rangelength="[0,500]" data-msg-rangelength="请输入500字以内的摘要"></textarea>
                        </div>
                    </div>
                </div>
            </div>
<!-- 图片上传   -->
            
            <!-- lyy 2016-11-4 demo 文件上传 -->


                                <span class="fileupload-new">选择文件</span>
                                <input type="file" name='files' >
                            

                                <span class="fileupload-new">选择文件</span>
                                <input type="file" name='files'>
                            

                                <span class="fileupload-new">选择文件</span>
                                <input type="file" name='files' >


            
            <div class="row-fluid">
                <div class="span12" style="text-align: center;" >
                    <button  type="submit" id="submitform" class="btn btn-green btn-padding"   >确  定</button>
                    <button type="button" id="test" class="btn btn-primary btn-padding "  onclick=" form_reset('add_book_form');" style=" margin-left: 20px;"> 重  置 </button>
                    <!-- $('add_book_form').data('validator').resetForm();-->
                    <button type="button"  class="btn btn-red btn-padding" data-dismiss="modal"   onclick=" form_reset('add_book_form')" style=" margin-left: 20px;"> 取  消 </button>
                </div>
            </div>
            </div>
        </form>
        </div>
</div>
</div>
采用 MultipartHttpServletRequest request获取文件
public ModelAndView doAdd(BookInfoEntity bookInfo, MultipartHttpServletRequest request) throws ParseException { 


//图片上传
            Iterator<String> itrImage = request.getFileNames();
            MultipartFile mpf = null;
            String fileName="";
            String[] fileNameAll={"未命名","jpg"};
            while (itrImage.hasNext()){
                mpf=request.getFile(itrImage.next());
                if(!StringUtil.isEmpty(fileName)){
                    fileNameAll=fileName.split("\\.");
                }else{
                    fileNameAll[0]="未命名";
                }
                try {
                    //String path = "images/book_images/";
                    //String realPath = request.getSession().getServletContext().getRealPath("/") +"/"+ path+"/";// 文件的硬盘真实路径
                    String realPath="D:\\ImageFiles\\book_images\\";
                    String newFileName;
                    SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
                    newFileName =bookName+df.format(new Date()) + "_" + new Random().nextInt(1000) + "." + fileNameAll[1];                  
                    String savePath = realPath +newFileName;// 文件保存全路径
                    savePath = java.net.URLDecoder.decode(savePath, "UTF-8");
                    FileCopyUtils.copy(mpf.getBytes(), new File(savePath));
                    bookInfo.setBookImage(newFileName);                 
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

暂时不用的图片们:


multipart继承关系

<p> </p>

multipart包结构
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,039评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,426评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,417评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,868评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,892评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,692评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,416评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,326评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,782评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,957评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,102评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,790评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,442评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,996评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,113评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,332评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,044评论 2 355

推荐阅读更多精彩内容