文件上传
1.文件上传的原理:
要实现Web开发中的文件上传功能,通常需完成两步操作:一是在Web项目的页面中添加上传输入项,二是在Servlet中读取上传文件的数据,并保存到目标路径中。
由于大多数文件的上传都是通过表单的形式提交给服务器的,因此,要想在程序中实现文件上传功能,首先要创建一个用于提交上传文件的表单页面。在表单页面中,需要使用<input type="file">标签在jsp页面中添加文件上传输入项。
<input type="file">标签
<input type="file">标签的使用需要注意以下两点:
为了方便处理用户上传的数据,Apache组织提供了一个开源组件Commons- FileUpload,该组件可以方便地将“multipart/form-data”类型请求中的各种表单域解析出来,并实现一个或多个文件的上传,同时也可以限制上传文件的大小等。Commons-FileUpload组件性能十分优异,并且使用非常简单。
2.Commons- FileUpload组件的工作流程:
2.1 FileItem接口
FileItem接口主要用于封装单个表单字段元素的数据,一个表单字段元素对应一个FileItem对象。Commons-FileUpload组件在处理文件上传的过程中,将每一个表单域(包括普通的文本表单域和文件域)封装在一个FileItem对象中。
2.2 DiskFileItemFactory类
DiskFileItemFactory类用于将请求消息实体中的每一个文件封装成单独的FileItem对象。如果上传的文件比较小,将直接保存在内存中,如果上传的文件比较大,则会以临时文件的形式,保存在磁盘的临时文件夹中。默认情况下,不管文件保存在内存还是磁盘临时文件夹,文件存储的临界值是10240字节,即10KB。
方法声明 | 功能描述 |
DiskFileItemFactory() | 采用默认临界值和系统临时文件夹构造文件项工厂对象 |
DiskFileItemFactory(int sizeThreshold,File repository) | 采用参数指定临界值和系统临时文件夹构造文件项工厂对象 |
上表中列举了DiskFileItemFactory类的两个构造方法,其中,第二个构造方法需要传递两个参数,第一个参数sizeThreshold表示文件保存在内存还是磁盘临时文件夹中的临界值,第二个参数repository表示临时文件的存储路径。
2.3 ServletFileUpload类
ServletFileUpload类是Apache组件处理文件上传的核心高级类,通过调用parseRequest(HttpServletRequest) 方法可以将HTML中每个表单提交的数据封装成一个FileItem对象,然后以List列表的形式返回。
方法声明 | 功能描述 |
ServletFileUpload() | 构造一个未初始化的ServletFileUpload实例对象 |
ServletFileUpload(FileItemFactory fileItemFactory) | 根据参数指定的FileItemFactory 对象创建一个ServletFileUpload对象 |
上表中列举了ServletFileUpload类的两个构造方法。在文件上传过程中,在使用第一个构造方法创建ServletFileUpload对象时,需要在解析请求之前调用setFileItemFactory()方法设置fileItemFactory属性。
3.动手实践:实现文件上传
3.1 测试页面:
<form action="/UploadServlet" method="post" enctype="multipart/form-data">
<table width="600px">
<tr>
<td>上传者:</td>
<td><input type="text" name="myText"></td>
</tr>
<tr>
<td>上传文件:</td>
<td><input type="file" name="myFile"></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="上传"></td>
</tr>
</table>
</form>
3.2 UploadServlet代码:
package com.yy.controller;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;
@WebServlet(name = "UploadServlet",urlPatterns = "/UploadServlet")
public class UploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
//防止浏览器打印中文,出现乱码问题
resp.setContentType("text/html;charset=utf-8");
//创建DiskFileItemFactory的对象 用于作为创建ServletFileUpload对象的参数
DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();
//创建ServletFileUpload的对象
ServletFileUpload fileUpload = new ServletFileUpload(fileItemFactory);
File f = new File("C:\\Users\\Administrator\\Desktop\\temp\\");
if (!f.exists()) {
f.mkdirs();
}
fileItemFactory.setRepository(f);
//设置编码格式
fileUpload.setHeaderEncoding("utf-8");
//解析req 对象 得到FileItem的集合
List<FileItem> fileItems = fileUpload.parseRequest(req);
//遍历fileItems集合 拿到每一个fileItem对象
for (FileItem fileItem : fileItems) {
//判断是否为普通字段
if (fileItem.isFormField()) {
//通过获取fileItem中的name属性值 与 前端的name属性值做对比
if (fileItem.getFieldName().equals("myText")) {
//获取name属性为myText 的控件输入的普通文本内容
String value = fileItem.getString("utf-8");
//浏览器打印
resp.getWriter().print("上传者:" + value + "<br/>");
}
} else if (fileItem.getFieldName().equals("myFile")) {
//获取上传的文件名
String filename = fileItem.getName();//study.PNG
if (filename != null && !filename.equals("")) {
//浏览器打印
resp.getWriter().print("上传的文件名:" + filename + "<br/>");
//保证文件名不重复 即保证上传的文件名字唯一
filename = UUID.randomUUID().toString().substring(0, 8) + "_" + filename; //uuid_study.PNG
//上传到服务器的发布路径
// String wenPath = "/upload/";
// String uploadPath = getServletContext().getRealPath(wenPath + filename);
//上传路径
String uploadPath = "C:\\Users\\Administrator\\Desktop\\photo\\" + filename;
//创建文件
File file = new File(uploadPath);
file.getParentFile().mkdirs();
file.createNewFile();
//获取上传文件流
InputStream in = fileItem.getInputStream();
FileOutputStream out = new FileOutputStream(file);
byte[] bytes = new byte[1024];
int len;
while ((len = in.read(bytes)) > 0) {
out.write(bytes, 0, len);
}
//关闭资源
in.close();
out.close();
//删除临时文件
fileItem.delete();
resp.getWriter().print("文件上传成功!");
}
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
}
需要注意的是,untitled2项目的out \artifacts\untitled2\upload目录是IDEA默认发布的路径目录,如果读者将项目发布目录配置在Tomcat的webapps中,可以去webapps中的untitled2项目中查看。
文件下载
2.1文件下载的过程
浏览器通常会直接处理响应的实体内容,需要在HTTP响应消息中设置两个响应消息头字段,用来指定接收程序处理数据内容的方式为下载。当单击“下载”超链接时,系统将请求提交到对应的Servlet。在Servlet中,首先获取下载文件的地址,并根据文件下载地址创建文件字节输入流,然后通过输入流读取要下载的文件内容,最后将读取的内容通过输出流写到目标文件中。
package com.yy.controller;
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
@WebServlet(name = "DownloadServlet",urlPatterns = "/DownloadServlet")
public class DownloadServlet extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置ContentType字段值
response.setContentType("text/html;charset=utf-8");
//获取所要下载的文件名称
String filename = request.getParameter("filename");
//下载文件所在目录
String folder = "/download/";
// 通知浏览器以下载的方式打开
response.addHeader("Content-Type", "application/octet-stream");
response.addHeader("Content-Disposition",
"attachment;filename="+filename);
folder=folder+filename;
// 通过文件流读取文件
InputStream in = getServletContext().getResourceAsStream(folder);
// 获取response对象的输出流
OutputStream out = response.getOutputStream();
byte[] buffer = new byte[1024];
int len;
//循环取出流中的数据
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
}
}
在untitled2项目的web目录下创建一个名称为download的文件夹,在该文件夹中放置一个名称为“1.png”的图片文件。
<body>
<a href=
"http://localhost:8080/untitled2/DownloadServlet?filename=1.png">
文件下载</a>
</body>