Java Web基础知识之文件上传

 其实关于文件上传在最早之前是使用Apache的Commons FileUpload组件,但是自从servlet提出了自己的解决办法之后,就不再使用这个组件了,有了正规军谁还使用民兵啊,不对,也不一定,之前Apache的HttpClient就比JDK自己的HttpUrlConnection流行

一、 客户端编程
下面是我们的页面FileUpload.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"  
    pageEncoding="ISO-8859-1"%>  
<!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=ISO-8859-1">  
<title>File upload</title>  
</head>  
<body>  
    <form action="/JavaServlet/fileUploadServlet" method="post" enctype="multipart/form-data">  
        select a file :<input type="file" name="file" multiple>  
        <input type="text" value="upload file" name="identifier" />  
        <input type="submit" value="upload" />  
    </form>  
</body>  
</html> 

关于这个页面有几点值得注意:
首先是action是URI,注意它和URL的区别,不要省掉contextPath,这样会找不到该资源的;
enctype的值一定是multipart/form-data,这个属性是指在发送放到服务器之前如何对表单数据进行编码,这种方式是将表单数据组装成一条消息,并用分隔符将表单的每个部分分隔开;默认值是application/x-www-form-urlencoded,也意味着所有的值都会进行编码,这种方式是用键值对来进行编码;
如果想要上传多个文件,可以使用multiple属性,注意这个属性是在HTML5中提出的,这样就不用我们使用多个input来上传多个文件了;
下面我上传三个文件,可以看到Http请求和响应如下,我使用的是chrome浏览器:
这里写图片描述
这里边最重要的就是Content-type,这个类型和表单的enctype类型相同,最重要的就是增加了boundary属性,该属性的值就是用来分割表单中各个部分的。
下面是post表单时发出的request payload,如下:
这里写图片描述
从图中可以看出整个被上传的表单数据是被分隔符包裹起来,并且通过使用”分隔符–”的方式来标明数据结束,这个分隔符在开头和结尾必须有又来说明数据的开始和结束,只有在表单中有多个元素或者上传多个文件时才会在中间出现,每一个被分隔符分隔的部分里面都包含Content-disposition首部,里面包含表单元素中的一些属性,有name,filename;但是content-type首部是可选的,而且对于表单中非文件的部分是没有content-type的,只有文件域才会有content-type这个首部。
二、 服务端编程
了解客户端是为了我们在服务端解析客户端发过来的请求,那么如何判断发过来的请求中是否包含文件呢?基于以下几点可以进行判断:
在一个由multipart/form-data组成的请求中,每一个部分包括非文件部分都会转换成一个Part对象,在服务器端我们主要是针对该Part对象进行处理;
通过查看Part中是否存在content-type首部来判断一个Part是属于普通的非文件部分,还是属于文件部分;
如果存在content-type,则说明文件部分存在,之后查看上传的文件名称是否为空,文件名为空说明有客户端没有选择要上传的文件;
如果文件存在,就使用Part的write方法来将他写入服务器端的文件系统;
在服务器上处理文件上传的servlet如下:

@WebServlet(name="fileUploadServlet", urlPatterns={"/fileUploadServlet"})  
//@MultipartConfig(location="/")  
@MultipartConfig  
public class FileUploadServlet extends HttpServlet {  

    private static final long serialVersionUID = 1920423365061691218L;  

    @Override  
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {  
        Collection<Part> parts = req.getParts();  

        for(Part part: parts){  
            if(part.getContentType() != null){  
                String filename = getFileName(part);  
                if(filename != null && !filename.isEmpty()){  
                    part.write(filename);  
                }  
            }   
        }  
    }  


    String getFileName(Part part){  
        Objects.requireNonNull(part, "part can not be null");  

        String disposition = part.getHeader("content-disposition");  
        String[] disParts = disposition.split(";");  

        String filenamePart = disParts[disParts.length - 1];  
        String filename = filenamePart.substring(filenamePart.indexOf("=")+1).trim().replace("\"", "");  

        return filename;  
    }  
}  

关于上述的处理其实主要围绕@MultipartConfig注解和Part接口来进行,关于这两个的使用其实很简单,可以查看一下JavaDoc即可,但是有两个我要着重说一下,因为我自己就掉坑里了:
一个就是@MultipartConfig中的location属性,这个绝对是一个坑,当这个值是一个绝对路径时,调用Part的write()方法将该文件写到对应的路径是没有问题的,但是当是相对路径的时候,比如如我上边写的”/”,这个相对路径是相对于tomcat路径下的C:\Program Files\tomcat7\work\Catalina\localhost\JavaServlet路径的,这是一个文件上传临时保存的位置,这个路径值主要是为了在文件超过预设大小时写入硬盘,为@MultiSizeThreshold准备,所以最好还是不要使用这个属性为好;
还有一个就是Part中的getName()方法并不是用来获取文件名的,而是用来获取表单元素中的name属性的;文件名需要我们自己来解析出来;
在使用Part的write()方法时,如果提供的是相对路径,那么相对路径的根路径都是C:\Program Files\tomcat7\work\Catalina\localhost\JavaServlet;
三、 其他问题
上面两个部分主要就是说明了客户端和服务端分别怎么做,但是根据具体的业务逻辑还有很多别的需求需要考虑,如下:
对文件的后缀名进行验证和约束;对文件的大小进行约束;文件的存储,是放在本地文件系统上还是数据库中;文件的编码问题,尤其是中文的编码问题;避免相同文件名的文件的重复上传导致覆盖问题;这些问题实现起来其实都比较简单,下面有一篇文章可以进行参考,主要是怕考虑不到这些问题,或者为了省事偷工减料。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值