1 文件上传
在很多业务场景,用户都需要上传头像、证件照以及其他文件。
文件上传就是用户通过浏览器选择需要上传的文件并将文件传输至网站服务器上的一个过程
文件上传需要对请求中传的的文件流进行解析,一般会使用第三方jar包对文件流进行解析
1.1 实现步骤
文件上传不同于常见的form表单数据提交,后端servlet不能直接从HttpServletRequest中取到文件,需要进一步解析请求头。
直接对request使用getParameter提取数据什么也取不到,要借助第三方jar包对请求头进行数据解析。
首先要导入第三方jar包,然后前端设置好文件上传的接口,最后在后端设置好对应的servlet。
1.1.1 在项目中加入jar包
文件上传相关的jar包
- commons-fileupload-1.3.3.jar
- commons-io-2.5.jar
jar包放置的地方:
1.1.2 创建页面
如果前端的文件的上传是同步请求,那么就要求用户填写完表单信息后将表单信息连带文件一起上传,在用户提交表单信息前,文件一直处于未上传状态,当用户提交信息时还得等待文件上传,前端功能不太友好。
所以我们可以把文件上传和普通表单提交的流程分开,通过工具把文件单独上传到服务器(异步上传),并在页面接收存储的地址(相对路径),用户填完数据提交表单时,文件已上传,只需要提交普通表单即可。
事实上下面使用的VUE+ElemenUI的文件上传方式已经实现了异步请求,用户点击提交时文件会单独进行上传,不会影响页面表单的数据填写。
使用VUE+ElementUI设置文件上传按钮
|
1.1.3 创建Servlet实现上传
步骤大致可以分为:
- 创建servlet
- 设置一个服务器端的地址(现阶段就是本机上某个文件夹的绝对路径) 用于存放用户上传的文件
- 从请求头中解析出用户提交的文件的二进制流
- 将文件的二进制流写入到步骤2指定的地址中的一个空的File对象中,实现文件上传的功能
1.1.3.1 创建servlet对象
使用@webServlet配置URL,给前端提供servlet地址,并且设置好请求头的编码格式,避免中文乱码
1.1.3.2 设置服务器存储文件的地址
设置用户提交文件到服务器时,文件存放在服务器上的位置,图片所示中使用了项目在tomcat上的地址,实际开发时需要按照业务去设置
request . getServletContext() . getRealPath("/") | 固定写法,从ServletContext类中获取到当前web项目在tomcat服务器上的根目录 |
获取到的是下图划线的文件夹地址
|
1.1.3.3 解析请求头 获取文件的二进制流 将二进制流写入指定对象
借助第三方jar包,判断以及解析前端返回的请求头中的信息,将用户上传的文件的二进制流写入到服务器的一个空的文件对象中,实现文件上传。
如果写入一个不为空的文件对象,则会发生文件覆盖情况。
ServletFileUpload . isMultipartContent(request) | 第三方jar包提供的方法,用于判断前端请求是否为: method=post、enctype="multipart/form-data",如果有一个不对就返回false |
DiskFileItemFactory fileItemFactory = new DiskFileItemFactory() | 第三方jar包提供的工厂对象,用于解析前端上传的文件 |
ServletFileUpload fileUpload = new ServletFileUpload(DiskFileItemFactory对象) | 第三方jar包提供的对象,用于上传处理过的DiskFileItemFactory对象,实现上传文件的功能 |
List<FileItem> fileItemList = fileUpload . parseRequest(request) | 将request对象中包含的表单信息(包含文件信息)存在FileItem对象数组中,这里使用List集合接收 |
FileItem对象 . getName() | 获取到当前FileItem对象指向的数据的名字,如果是文件就是文件名 |
FileItem对象 . getFieldName() | 获取到当前FileItem对象指向的数据在表单的名字,如果是文件,基本都是"file" |
FileItem对象 . getSize() | 获取到当前FileItem对象指向的数据的大小,单位:字节 |
FileItem对象 . isFormField() | 判断当前FileItem对象指向的是否是普通的表单元素,如果返回false代表指向的是一个文件 |
FileItem对象 . getString() | 获取到当前FileItem对象指向的数据的值,如果是表单元素则返回value,如果是文件则返回文件的二进制流 |
FileItem对象 . write(File对象) | 将FileItem对象复制到指定的File对象中,如果File对象已经实际存在,那么会被覆盖 |
对上述方法的补充说明
①上述方法以及对象都是基于第三方jar包实现的
|
②isMultipartContent(request)方法的源码说明:
|
reques.getContentType()判断的是图中的数据,图示位置在浏览器请求头中
|
③如果不设置请求头的编码格式,那么getName()获取到的中文会乱码
④对一个文件使用getFieldName(),获得的值为file,原因如下:
在回答“为什么对一个文件使用getFieldName(),获得的值为file”这个问题之前 我们需要先了解文件上传的方式的实质方式
我们学习的文件上传方式基于ElementUI的文件上传组件
![](https://img-blog.csdnimg.cn/2021072421325465.png)
这个组件看起来简单,但是内部使用了form表单代码,只是使用了隐藏域不显示,而且也没有在官网进行说明
表单的提交方式为post,enctype为multipart/form-data,点击文件上传按钮时,其实是执行form提交,会把选择到的文件以二进制数据形式发送到服务器。
代码:
|
实际上传文件时使用F12-审查元素 查看页面代码可以看到上传的文件写在一个input表单元素中 type=file name=file
|
因此当后端接收到请求头时,不能直接通过请求头读取到数据,需要使用第三方jar包对请求头进行解析,具体解析源码这里就不分析了
总之解析出来后的FileItem对象就会指向表单中的各个元素,如果是文件 那么就是type=file的表单元素 如果不是文件 就是type=text的表单元素
这就是为什么对一个文件使用getFieldName(),获得的值为file
因为这个方法“获取到当前FileItem对象指向的数据在表单的名字”
⑤对一个文件使用getString(),获取到的是一长串的二进制字符串,打印出来时显示为乱码
|
⑥上述方法中部分get方法的返回情况
| |
代码:
这里的代码为整个文件上传功能的核心代码,在这之前的代码见前面的2.1.3.1 以及 2.1.3.2的截图