话说
各位读者盆友,中午好!今日小结下文件上传

目录:


自己处理文件上传的时候,多用2种方式:
1.框架方式(SpringMVC)
2.Servlet方式
1)仅只有文件上传域
2)既有普通上传域,又有文件上传域


难度系数:★★☆☆☆

今天对这2中方式做个整体总结。
1.框架方式(SpringMVC)
用框架,一切都显得那么容易了。我们先来容易的。其实类似JDBC,文件上传也是固定的步骤。


1)导包
2)写前端
3)配置springmvc.xml
4)写方法
4.1 获取上传文件名
4.2 得到前、后缀名
4.3 创建输入、输出流(路径)
4.4 CopyFile
4.5 写入数据库
4.6 页面显示


应用场景:比如,我们添加商品的时候,需要添加商品信息和图片,那么这个需求就是:一个form表单里面,既有普通表单项,又有文件上传表单项,看起来比较简单,实际上如果用后面Servlet方式,还是有不少坑的。只是框架帮我们简化了许多。

java Document document 上传文件服务器_System

1)导包


commons-fileupload-1.3.2.jar
commons-io-2.5.jar


文件上传,本质就是IO流的操作;加上我们要用人家文件上传的工具类,所以这2个包都是必须的。

2)写前端

<input  type="file" name="file" value="添加图片" accept="image/png,image/gif,image/jpg,image/jpeg">

<form method="post"   action="addGoods2"  enctype="multipart/form-data">

首先搞定单个文件上传域,name很重要!这个name就是后面的MultipartFile的别名,必须一致,否则找不到。这和我们通过name得到表单value原理一致嘛。
enctype=”multipart/form-data” 这个非常重要!表明表单不是普通的表单,而是要用二进制处理(流)来处理的表单。

3)配置springmvc.xml

<!--配置文件上传 解析器 -->
 <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
            <!-- 设置文件最大尺寸为5M  -->
            <property name="maxUploadSize">
                <value>5242880</value>
            </property>
        </bean>

这里的Id必须有,属性可以视情况而定。怎么找到类全名呢?
Eclipse中:Ctrl + Shift + T 打开搜索,输入CommonsMultipartResolver即可!这也是一个解析器,类似视图解析器之类的东东。

4)写方法


4.1 获取上传文件名
4.2 得到前、后缀名
4.3 创建输入、输出流(路径)
4.4 CopyFile
4.5 写入数据库
4.6 页面显示


@RequestMapping(value="addGoods2",method=RequestMethod.POST)
    public String addGoods2(Goods goods,MultipartFile file) throws Exception {
        if(file != null) {
            //1)获取上传文件名称
            String oriName =    file.getOriginalFilename();//文件名+后缀名 100.png//单纯实现效果,这样就可以了!但是在不同位置同名同后缀的图片或者文件太正常了,但是一旦存入,后者就会把前者覆盖掉;这是我们不愿意看到的,比如:很多人名字都是小明,但是是不同的个体哈。

                //2)修改名称
                SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhhmmss");
                String picName3 =   sdf.format(new Date());//还可以用另外2中方式命名:
                String extName = oriName.substring(oriName.lastIndexOf("."));//获取的图片后缀名是:.jpg
                FileOutputStream fos = new FileOutputStream("F:\\img5"+"\\"+"小美截图"+picName3+extName);

                //3)写入本地存储路径
                FileCopyUtils.copy(file.getInputStream(), fos);

                //4)图片路径存入数据库
                goods.setGoodsImg("小美截图"+picName3+extName);//这才是存入数据库的形式
                goodsService.addGoods(goods);

            }
                return "redirect:goodsList";
        }

        /**
         * 以上代码小结:
         * 1.MultipartFile.getName()是获取表单项的name,也就是<input  type="file" name="file" value="添加图片" >这里面的name
         * 2.getOriginalFilename()获取的是你上传文件名:eg:100.png
         * 3.要避免同名图片(文件)覆盖,可以自由命名 在下总结了3种方式,可以自由选择:
         *  法1:UUID  1f7dc2e572844e959e4c868c92ee9b7b.png
         *   法2:时间戳    1516887657528.png
         *  法3:模仿QQ截图:QQ截图20180125214926.png  ==》 小美截图20180125215052.png
         * 具体实现方式:法1:String picName = UUID.randomUUID().toString().replace("-", "");//UUID产生的随机文件名:1f7dc2e5-7284-4e95-9e4c-868c92ee9b7b 去掉中间-后的结果是:1f7dc2e572844e959e4c868c92ee9b7b
         *          法2:Long picName2 = System.currentTimeMillis();//时间戳方式命名
         * 
         * 总结:SpringMVC实现单个文件上传核心就3步:
         * 1步:FileCopyUtils.copy(inputStream, outPutStream);
         * 2步:给一个输出流:完整路径名
         * 3步:上传数据库
         */ 
--------------------------------------------------------------------------------

页面展示:用绝对路径:类似于这样的:http://192.168.11.123/配置虚拟路径名/数据库存储图片名

<%
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort();
%>

当然有时候会也会用到获取项目名称:request.getContextPath()直接拼接即可,这里我们的项目名称已经被我们取的img5所替代,所以用不着这个。

getScheme() ——获取协议名:http
getServerName()——获取服务器地址,也就是类似:localhost 192.168 本机IP地址
getServerPort()——端口号 8080 或者80

需要特别注意的是:我们在输出流确定路径的时候,是直接在本地磁盘某个位置写入的,实际工作中直接写入服务器某个位置:要做如下配置:图形化界面配置或者修改配置文件都可以。

法1:图形化界面

java Document document 上传文件服务器_System_02

DocumentBase 就是配置文件、图片实际上传到本地的位置,自己定义
Path就是抽象后的路径,项目中就用path来访问即可。
每次手动配置过后,自动会在Servler.xml中增加配置,和配置Servler.xml本质相同!

法2:修改配置文件

java Document document 上传文件服务器_文件上传_03

<Context docBase="store" path="/store" reloadable="true" source="org.eclipse.jst.jee.server:store"/>
  <Context docBase="F:\img3" path="/img3" reloadable="true"/>
 <Context docBase="F:\img4" path="/img4" reloadable="true"/>

docBase表示图片真实存放在本地的位置;path表示项目中用这个去实际访问。
页面上这么 访问即可。

<img src="<%=basePath2 %>/img5/${goods.goodsImg }" style="width:50px;height:50px">

2.Servlet方式

上面操作简单,下面Servlet的方式是入门级的方式,当然配置路径方法和上面相同,重点在于使用手段有差别。分为2中情况。

1)仅只有文件上传域
2)既有普通上传域,又有文件上传域
步骤和上面类似:
导包==》页面表单enctype=”multipart/form-data” ==>写方法 因为类似,省略。只展示核心代码。
1)仅只有文件上传域

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    User user = (User)req.getSession().getAttribute("user");
    int userId = user.getUserId();
    //1.判断是否是文件上传域表单
    boolean isMultiPart =   ServletFileUpload.isMultipartContent(req);
    if(isMultiPart) {
        //2.是的话,穿件文件上传对象ServletFileUpload
        ServletFileUpload sfu = new ServletFileUpload(new DiskFileItemFactory());
        try {
            //3.处理form表单提交过来的请求
            List<FileItem> listFileItem =   sfu.parseRequest(req);
            //4.遍历每单个表单项,判断是否是文件上传表单项
            for(FileItem item:listFileItem) {
                //如果不是普通的表单域(是文件上传域)
                if(!item.isFormField()) {
                    //5.获取上传图片(文件)名称
                    String fileName =   item.getName();
                    System.out.println("文件名:"+fileName);//文件名:100.png
                    String path = "F:\\img2";
                    File file = new File(path);
                    //6.穿件路径
                    String path2 = file + "\\"+fileName;
                    File file2 = new File(path2);
                    //7.写入
                    item.write(file2);

                    //8.把路径存放数据库 
                    UserRegisterService urs = new UserRegisterServiceImpl();
                    String sql = "update user set fileName = ? where userId = ?";
                    Object[] params = {fileName,userId};
                    urs.Cud(sql, params);

                    //修改一下session
                    user.setFileName(fileName);

                }
            }
                    //跳转页面
                    req.getRequestDispatcher("editUserInfo.jsp").forward(req, resp);

                } catch (FileUploadException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }

}

通过上面可以发现,尤其是路径那部分,每次都要File new File很是麻烦,其他注意点和SpringMVC差不多了。

2)既有普通上传域,又有文件上传域

如果我们用ServletFileUpload来处理的话,一旦遇到这种情况,比如这个方法:
页面是这样的:

java Document document 上传文件服务器_文件上传_04

//2.2执行 新增操作
    @RequestMapping(value="addGoods",method=RequestMethod.POST)
    public String addGoods(Goods goods,HttpServletRequest req) throws Exception {

        String fileName = "";
        //上传图片
        //凭借商品属性值Id
        String attrValIds = "";


        boolean isMultiForm =   ServletFileUpload.isMultipartContent(req);

这样存在什么问题?那就是页面传过来的goods整个对象没发直接默认接收,因为全部变成了二进制流!所以,如果是一个对象,那么这个对象所有的属性需要重新接收一遍,否则整个对象获取不到。

这个时候,代码是这样的:

//2.2执行 新增操作
@RequestMapping(value="addGoods",method=RequestMethod.POST)
public String addGoods(Goods goods,HttpServletRequest req) throws Exception {

    String fileName = "";
    //上传图片
    //凭借商品属性值Id
    String attrValIds = "";


    boolean isMultiForm =   ServletFileUpload.isMultipartContent(req);
    if(isMultiForm) {   
        System.out.println("获取商品图片路径:进来了!!!");
        ServletFileUpload sfu =  new ServletFileUpload(new DiskFileItemFactory());
        List<FileItem> fileItemsList =  sfu.parseRequest(req);
        for(FileItem fileItem : fileItemsList) {
            if(!fileItem.isFormField()) {
                //写上传图片代码
                String path="F:\\img4";
                File file = new File(path);
                fileName = fileItem.getName();
                System.out.println("上传图片名字是:"+fileName);
                String path2 = file + "/"+fileName;
                File file2 = new File(path2);
                fileItem.write(file2);
                goods.setGoodsImg(fileName);
                System.out.println("赋值后的数据库商品路径:"+goods.getGoodsImg());

                }else {


                if("goodsName".equals(fileItem.getFieldName())) {

                    String goodsName = fileItem.getString("utf-8").trim();
                    goods.setGoodsName(goodsName);
                    System.out.println("获取到的商品名称为======================:"+goodsName);
                }


                if("attributeValId".equals(fileItem.getFieldName())) {
                    String  attributeValId =    fileItem.getString();
                    System.out.println("获取到的属性值Id数组为:======================:"+attributeValId);
                    attrValIds +=attributeValId+",";
                }
                if("categoryId".equals(fileItem.getFieldName())) {
                    String  categoryId =    fileItem.getString();
                    System.out.println("获取到的分类Id为:======================:"+categoryId);
                    if(categoryId != null && categoryId != "") {

                        goods.setCategoryId(Integer.parseInt(categoryId));
                    }
                }
                if("goodsTagId".equals(fileItem.getFieldName())) {
                    String  goodsTagId =    fileItem.getString();
                    System.out.println("获取到的商品标签Id为:======================:"+goodsTagId);
                    if(goodsTagId != null && goodsTagId != "") {

                        goods.setGoodsTagId(Integer.parseInt(goodsTagId));
                    }
                }


                //===================================================

                if("goodsCount".equals(fileItem.getFieldName())) {
                    String  goodsCount =    fileItem.getString();
                    System.out.println("获取到的商品数量为:======================:"+goodsCount);
                    if(goodsCount != null && goodsCount != "") {

                        goods.setGoodsCount(Integer.parseInt(goodsCount));
                    }
                }
                if("goodsPrice".equals(fileItem.getFieldName())) {
                    String  goodsPrice =    fileItem.getString();
                    System.out.println("获取到的商品价格为:======================:"+goodsPrice);
                    if(goodsPrice != null && goodsPrice != "") {

                        goods.setGoodsPrice(Double.parseDouble(goodsPrice));
                    }
                }
                if("goodsSalePrice".equals(fileItem.getFieldName())) {
                    String  goodsSalePrice =    fileItem.getString();
                    //System.out.println("获取到的商品价格为:======================:"+goodsSalePrice);
                    if(goodsSalePrice != null && goodsSalePrice != "") {

                        goods.setGoodsSalePrice(Double.parseDouble(goodsSalePrice));
                    }
                }

                if("goodsUrl".equals(fileItem.getFieldName())) {

                    String goodsUrl = fileItem.getString("utf-8").trim();
                    goods.setGoodsUrl(goodsUrl);
                    //System.out.println("获取到的商品名称为======================:"+goodsUrl);
                }
                if("goodsBrief".equals(fileItem.getFieldName())) {

                    String goodsBrief = fileItem.getString("utf-8").trim();
                    goods.setGoodsBrief(goodsBrief);
                    //System.out.println("获取到的商品名称为======================:"+goodsBrief);
                }
                if("goodsDetail".equals(fileItem.getFieldName())) {

                    String goodsDetail = fileItem.getString("utf-8").trim();
                    goods.setGoodsDetail(goodsDetail);
                    //System.out.println("获取到的商品名称为======================:"+goodsDetail);
                }




                }



            }
            System.out.println("拼接后的属性字符串为:"+attrValIds);
            goods.setAttrValIds(attrValIds);
            goodsService.addGoods(goods);   

        }

            }

            return "redirect:goodsList";
    }

既然都是二进制流,所以通通要按照二进制流的方式在接收每一个属性.直接通过request.getParameter()是没法接收的,因为是二进制。所以框架的整个便利都失效了,属性得一个个接收,仿佛回归到了JDBC…….你想想,这是多么“浩瀚”的工程!对比SpringMVC,真是太繁琐了!

小结:Servlet文件上传


文件上传四步走:
1.form表单
enctype=”multipart/form-data”

2.导包
commons-fileupload-1.3.2.jar commons-io-2.5.jar

3.写Servlet
核心:ServletFileUpload对象
步骤:1)判断表单是否是文件上传表单类型:ServletFileUpload.isMultipartContent(req);
2)创建ServletFileUpload对象:new ServletFileUpload(new DiskFileItemFactory());

List<FileItem> list集合。

4)判断此文件上传类型表单中的表单项是否是文件上传项:是的话,在在遍历(因为可能有多个文件)
这里一定要注意!if(!item.isFormField()) 这里的isFormField()表示是普通表单项;我们要对文件夹上传的表单项遍历,所以要用!item.isFormField()

5)得到上传文件名:item.getName();

6)创造路径:String path = “F:\img”; String path2 = file + “/”+fileName; File storeFile = new File(path2);

7)写入即可 item.write(storeFile);

8)把路径存放到数据库:String sql = “update user set fileName = ? where userId = ?”;

4.页面显示
注意,图片在服务器端,是无法通过绝对路径访问的,所以需要

<% String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort(); %>

得到上下文路径,然后:

<img src="<%=basePath%>/img/${user.fileName}">

这里要特别注意路径问题:我们没有用虚拟路径,是因为虚拟路径上传后,代码一旦变动,临时创建的文件夹就会自动消失,图片找不到!所以在写死路径之前,要这么操作下:
操作 同SpringMVC,略!