上传文件
基础知识
- 要有一个form标签,用于向服务器发送表单数据;form表单的method属性为post。
传送方式 | 数据大小 |
---|---|
GET | 当发送数据时,GET 方法向 URL 添加数据;URL 的长度是受限制的(URL 的最大长度是 2048 个字符)。 |
POST | 发送数据无限制。 |
安全性 | 与 POST 相比,GET 的安全性较差,因为所发送的数据是 URL 的一部分。在发送密码或其他敏感信息时绝不要使用 GET ! |
- form标签的encType 属性值必须为 multipart/form-data 值,encType=multipart/form-data 表示提交的数据,以多段(每一个表单项一个数据段)的形式进行拼接,然后以二进制流的形式发送给服务器。
- form标签中的input type=“file” 添加上传的文件。
<form action="/JSTL//uploadImg" method="post" enctype="multipart/form-data">
<label>用户名:</label><input name="username" placeholder="用户名" type="text"> <br>
<label>头像:</label><input type="file" name="photo"><br>
<input type="submit" value="提交">
</form>
依赖包
- commons-fileupload-1.2.1.jar
- commons-io-1.4.jar
功能分析
- 首先,服务器接收图片的数据是多端传输的,因此判断上传的数据是否为多段传输(只有多段传输数据,才是文件上传的)
使用ServletFileUpload.isMultipartContent(request)检查数据是否符合传输格式。 - 当数据格式符合时,就需要解析接收到的数据。
- 对数据进行数据流转换,并保存。
功能实现
- ServletFileUpload
org.apache.commons.fileupload.servlet.ServletFileUpload类是Apache文件上传组件处理文件上传的核心高级类(所谓高级就是不需要管底层实现,暴露给用户的简单易用的接口)
ServletFileUpload类构造方法 | ||
---|---|---|
方法 | 说明 | |
public ServletFileUpload() | 构造一个未初始化的实例,需要在解析请求之前先调用setFileItemFactory()方法设置 fileItemFactory属性。 | |
public ServletFileUpload(FileItemFactory fileItemFactory) | 构造一个实例,并根据参数指定的FileItemFactory 对象,设置 fileItemFactory属性。 | |
ServletFileUpload类常用方法 | ||
public void setSizeMax(long sizeMax) | setSizeMax方法继承自FileUploadBase类,用于设置请求消息实体内容(即所有上传数据)的最大尺寸限制,以防止客户端恶意上传超大文件来浪费服务器端的存储空间。其参数是以字节为单位的long型数字。 | 在请求解析的过程中,如果请求消息体内容的大小超过了setSizeMax方法的设置值,将会抛出FileUploadBase内部定义的SizeLimitExceededException异常(FileUploadException的子类)。该方法有一个对应的读方法:public long getSizeMax()方法。 |
public void setFileSizeMax(long fileSizeMax) | setFileSizeMax方法继承自FileUploadBase类,用于设置单个上传文件的最大尺寸限制,以防止客户端恶意上传超大文件来浪费服务器端的存储空间。其参数是以字节为单位的long型数字。该方法有一个对应的读方法:public long geFileSizeMax()方法。 | 在请求解析的过程中,如果单个上传文件的大小超过了setFileSizeMax方法的设置值,将会抛出FileUploadBase内部定义的FileSizeLimitExceededException异常(FileUploadException的子类)。 |
public List parseRequest(javax.servlet.http.HttpServletRequest req) | parseRequest 方法是ServletFileUpload类的重要方法,它是对HTTP请求消息体内容进行解析的入口方法。它解析出FORM表单中的每个字段的数据,并将它们分别包装成独立的FileItem对象,然后将这些FileItem对象加入进一个List类型的集合对象中返回。 | 该方法抛出FileUploadException异常来处理诸如文件尺寸过大、请求消息中的实体内容的类型不是“multipart/form-data”、IO异常、请求消息体长度信息丢失等各种异常。每一种异常都是FileUploadException的一个子类型。 |
public FileItemIterator getItemIterator(HttpServletRequest request) | getItemIterator方法和parseRequest 方法基本相同。但是getItemIterator方法返回的是一个迭代器,该迭代器中保存的不是FileItem对象,而是FileItemStream 对象,如果你希望进一步提高新能,你可以采用getItemIterator方法,直接获得每一个文件项的数据输入流,做底层处理;如果性能不是问题,你希望代码简单,则采用parseRequest方法即可。 | |
public stiatc boolean isMultipartContent(HttpServletRequest req) | isMultipartContent方法方法用于判断请求消息中的内容是否是“multipart/form-data”类型,是则返回true,否则返回false。isMultipartContent方法是一个静态方法,不用创建ServletFileUpload类的实例对象即可被调用。 | |
getFileItemFactory()和setFileItemFactory(FileItemFactory) | 方法继承自FileUpload类,用于设置和读取fileItemFactory属性。 | |
public void setProgressListener(ProgressListener pListener) | 设置文件上传进度监听器。该方法有一个对应的读取方法:ProgressListener getProgressListener()。 |
判断数据是否为多段传输:
ServletFileUpload.isMultipartContent(request)
- 解析上传的数据,得到每个表单项FileItem
将请求消息实体中的每一个项目封装成单独的DiskFileItem (FileItem接口的实现) 对象的任务,由 org.apache.commons.fileupload.FileItemFactory 接口的默认实现org.apache.commons.fileupload.disk.DiskFileItemFactory 来完成。
//创建FileItemFactory工厂实现类
//public DiskFileItemFactory() 采用默认临界值和系统临时文件夹构造文件项工厂对象。
FileItemFactory fileItemFactory = new DiskFileItemFactory();
//public ServletFileUpload(FileItemFactory fileItemFactory)
//构造一个实例,并根据参数指定的FileItemFactory 对象,设置 fileItemFactory属性。
//创建用于解析上传数据的工具类--ServletFileUpload类
ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
执行成功后,将数据存储到List中并保存数据
UpLoadServlet完整代码
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//判断上传的数据是否为多段传输(只有多穿传输数据,才是文件上传的)
if (ServletFileUpload.isMultipartContent(request)) {
//创建FileItemFactory工厂实现类
FileItemFactory fileItemFactory = new DiskFileItemFactory();
//创建用于解析上传数据的工具类--ServletFileUpload类
ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
try {
//解析上传的数据,得到每个表单项FileItem
List<FileItem> fileItems = servletFileUpload.parseRequest(request);
//循环判断每个表单项是普通类型,还是上传的文件
for (FileItem fileItem : fileItems) {
if (fileItem.isFormField()) {
//如果是普通表单项
System.out.println("表单项的name属性值:" + fileItem.getFieldName());
System.out.println("表单项的value属性值:"+fileItem.getString("UTF-8"));
} else {
//上传的是文件
System.out.println("表单项的name属性值:" + fileItem.getFieldName());
System.out.println("上传文件名:" + fileItem.getName());
fileItem.write(new File("e:\\"+fileItem.getName()));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
文件下载
基础知识
- 既然文件要发给客户端进行下载,那么我们首先要告诉客户端要将文件进行下载存储:response.setHeader(“Content-Disposition”,“attachment;filename=”+Filename);
其中,filename是指定存储文件时的文件名; - 告诉客户端服务器返回的数据的类型:response.setContentType("文件类型“)。
- http协议是不支持中文,因此各种浏览器采用的编码格式,火狐使用Base64进行编码,而chorme和IE采用URLEncode进行编码。当编码格式不正确时,http数据将出现乱码,下载文件名包含中文时文件名会出现乱码状况。
- 采用servletContext对象对需要传输的文件内容进行读取。
由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象。
废话不多说了,过程和上传文件没太大差别,我们就直接上代码!!
DownLoadServlet完整代码
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取要下载文件的文件名
String downFileName = "forest.pdf";
//2.读取要下载的文件的内容(通过servletContext对象可以读取)
ServletContext servletContext = getServletContext();
//3.获取要下载的文件的类型
String mimeType = servletContext.getMimeType("fileOpretion/file/" + downFileName);
System.out.println("要下载的文件类型为:" + mimeType);
//4.回传前,通过响应头告诉客户端返回数据的类型
resp.setContentType(mimeType);
//5.告诉客户端收到的数据数用于下载的(还是使用响应头)
//Content-Disposition响应头表示收到的数据怎么处理
//attachment表示附件,表示下载使用
//filename = 表示指定下载的文件名
//火狐浏览器使用Base64进行编码,chorme和IE使用URLEncode进行编码
resp.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode("刘.pdf","utf-8"));
/**
* "/"被服务器解析为http://ip:port/工程名/
*/
InputStream resourceAsStream = servletContext.getResourceAsStream("fileOpretion/file/" + downFileName);
//获取响应的输出流
OutputStream outputStream = resp.getOutputStream();
//把下载内容给客户端
//读取输入流中全部数据,复制给输出流,输出给客户端
int copy = IOUtils.copy(resourceAsStream, outputStream);
}