【JavaWeb-13】文件上传DiskFileItemFactory、ServletFileUpload以及注意事项、文件下载及注意事项

1、文件上传我们用的是Apache的common组件,里面有个fileupload的jar包,因为用到输出输入流,所以需要导入io的jar包。

——要上传文件,form表单里面需要增加enctype属性,并设置为multipart/form-data

——我们之前用request.getParameter来获取form表单里面的值,因为是字符输入流,但是如果设置成上面那样的话,就取不到值了,因为变成字节输入流了。

——所以本质上是我们把所有的内容当做字节输入流获取到,然后再用字节输出流来输出。
这里写图片描述

——核心代码是:

// Create a factory for disk-based file items
DiskFileItemFactory factory=new DiskFileItemFactory();
// Create a new file upload handler
ServletFileUpload upload=new ServletFileUpload(factory);
try {
    List<FileItem> items=upload.parseRequest(request); 
    for(FileItem item : items){
        if(item.isFormField()){
        // 普通表单
        }else{
        // 文件表单
            InputStream is=item.getInputStream();
            FileOutputStream fos=new FileOutputStream(file);
            int len=0;
            byte[] b=new byte[1024];
            while((len=is.read(b))!=-1){
                fos.write(b, 0, len);
            }
        }
    }
} catch (FileUploadException e) {
    e.printStackTrace();
}

——需要注意的是,我们存放文件路径的目录最好是在WEB-INF里面,用户无法访问到的路径,否则如果用户上传了一个存有代码的文件,然后访问这个文件相当于执行里面的代码,后果就糟糕了。

——另一个,我们对于文件的存放最好是分流一下,按照日期等方式,本案例中是按照upload/year/month/day/来存放的。
这里写图片描述

——还有一个,我们最好修改一下文件名,一个是防止重名,另一个是防止文件名里面有中文字符,引起不必要的麻烦。

——获取filename的时候,在IE等个别浏览器下,获取的是文件的路径,而不是文件名。所以我们需要处理一下这个文件名。如下就是处理的方法。

String filename=item.getName();
if(filename!=null){
    filename=FilenameUtils.getName(filename);
}

——另一个要养成的习惯,就是设置上传文件的大小,可以用ServletFileUpload.setFileSizeMax设置单个文件的限制,或者ServletFileUpload.setSizeMax设置总的上传大小。设置了大小之后,就需要捕获这个异常,然后处理这个异常,也就是返回错误信息给用户。

——判断文件名是否为空,可以判断yoghurt是否上传了文件。

——DiskFileItemFactory上传时有一个默认缓存,大小是10K,超过10K,就用磁盘作为缓存,存放缓存的目录默认是系统临时目录。当然,你也可以利用factory.setRepository()来设置缓存目录。

——最后一点,我们在截图里面没有写的代码,就是关闭io流

fos.close();
is.close();

案例源代码:JavaEE 文件上传代码示例

2、文件上传知识点补充。

——上面说到,我们用的是如果是文件对象,那我们就先获取输入流,然后拼接好文件路径后,用输出流写到文件里面,这就是上传的原理。但是如果用输入输出流的话,我们就需要手动关闭。这里我们可以用另一个FileItem自带的write方法来写入文件。

File file=new File(directory,filename);
try {
    item.write(file);
} catch (Exception e) {
    e.printStackTrace();
}
// 删除缓存文件
item.delete();

——还有一个头疼的问题就是解决文件名里面中文字符的问题。解决文件的中文名用以下两种方法都行,但第二种优先级高一些。

// 这是我们之前一直用的
request.setCharacterEncoding("UTF-8");
// 这是设置了ServletFileUpload 
ServletFileUpload upload=new ServletFileUpload(factory);
upload.setHeaderEncoding("UTF-8");

——那么普通表单的中文字符还是new String方法来解决?当然可以,如下。

new String(item.getString().getBytes("iso-8859-1"),"UTF-8")

但是,FileItem也为我们提供了一个快捷的方式,getString是可以加编码参数的,如下:

// 普通表单
                        System.out.println(item.getFieldName()+":"+item.getString("UTF-8"));

3、动态添加删除上传按钮。input需要增加name属性,否则取不到值。

  <head>
    <script type="text/javascript">
        function addBtn(){
            var d=document.getElementById("newDiv");
            d.innerHTML+="<div>文件:<input type='file' name='avatar' /><input type='button' value='删除' onclick='delBtn(this)' /></div>";
        }
        function delBtn(input){
            input.parentNode.parentNode.removeChild(input.parentNode);
        }
    </script>
  </head>

  <body>
    <form action="${pageContext.request.contextPath }/servlet/FileUploadServlet" method="post" enctype="multipart/form-data">
        用户名:<input type="text" name="username"/><br> 
        <div>
            文件:<input type="file" name="avatar" /><input type="button" value="添加" onclick="addBtn()" /><br>
        </div>
        <div id="newDiv">

        </div>
        <input type="submit" value="提交"/>
    </form>
  </body>

4、下载案例。

——里面需要注意的是中文字符的处理。一个是针对不同浏览器中文文件名的处理。还有一个就是输出内容里面的中文处理。见下面代码。

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //先整一个文件
        String filename="销售排行.csv";

        // 文件名中文的处理,针对不同浏览器的不同处理
        if(request.getHeader("user-agent").toLowerCase().contains("msie")){
            filename=URLEncoder.encode(filename, "UTF-8");
        }else{
            filename=new String(filename.getBytes("UTF-8"),"iso-8859-1");
        }

        // 告诉浏览器是下载,而不是在浏览器中打开
        response.setHeader("content-disposition", "attachment;filename="+filename);
        // 下面这种方法,比直接用response.setHeader("content-type",MIME)要好,因为我们一般不记得MINE,下面是根据文件名得到MIME
        response.setContentType(this.getServletContext().getMimeType(filename));

        // 告诉浏览器的编码格式
        response.setCharacterEncoding("UTF-8");

        // 整一个输出流和内容(输出到文件中的)
        PrintWriter out=response.getWriter();
        out.write("美的,2000\n");
        out.write("格力,1800\n");
        out.write("三星,1400\n");
        out.write("苹果,800\n");
        out.write("魅族,600\n");
    }

5、注意点。

——我们之前在上传的时候,把文件上传到WEB-INF里面了,是保证了安全,但是后续我们自己再使用的时候也不是很方便,所以还是在一般的upload或者media文件夹里就可以了。那么就需要我们在用户上传的时候做一些后缀判断之类的操作,以降低风险。

String extension=FilenameUtils.getExtension(filename);
if("jsp".equal(extension)||"exe".equal(extension)){
    ……
}

6、实际开发中的逻辑思路。比如在后台新增一本书,除了一些表单填写我啊,需要上传封面图片。

——我们在新增图书的servlet中,自己新建一个Map<String,String[]>,然后FileItem循环判断如果是普通表单的话就把name和value一起put进这个Map,如果是文件上传表单的话,把name和文件路径put到这个Map。最后利用BeanUtils把Map里面的数据都映射到book类中。最后再调用其他业务逻辑,把这个book传递出去。

Book book=new Book();
BeanUtils.populate(book,Map);
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值