文件上传下载
什么是上传和下载?
数据上传是指客户端向服务器上上传数据,客户端向服务器发送的所有请求都属于数据上传,文件上传是数据上传的一种特例,指客户端从向服务器上传文件。即将保存在客户端的文件上传至服务器中一个副本,保存到服务器中。
数据下载是指客户端从服务器上获取数据的过程。文件下载就是数据下载的一种特例,指客户端从服务器下载文件,即将原来保存在服务器中的文件下载到客户端中一个副本保存。通常我们对服务器所发出的请求,大多是文件下载请求,从服务器下载文本,图片,声音,视频等文件。然后由客户端浏览器对这些文件进行解析后,才可能看到这些多媒体信息。
但我们这里说的文件下载,指的是文件从服务器下载到浏览器后,浏览器并不直接解析,而是以附件的形式保存到客户端。
上传与下载的文件可以是文本文件,图片,声音,视频等各种类型。
文件上传的实现
文件上传要求客户端表单提交特殊的请求--multipart请求,即包含多部份数据的请求。所以文件表单对于表单数据的编码类型要求,必须为/multipart/form-data,即要为<form/>标签指定enctype属性值为“multipart/form-data”。enctype,即encoding type,编码类型。表单中要有file表单元素。
由于客户端上传文件的大小是不确定的,所以HTTP协议规定,文件上传的数据要存放于请求正文中,而不能出现在URL的地址栏中,因为地址栏中可以存放的数据量太小。也就是说,文件上传的表单,必须提交POST请求,而不能提交GET请求。
接收并将上传的文本文件显示到网页
RegisterServlet
public class RegisterServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 以输出流的形式获取multipart请求的请求体内容
ServletInputStream is = request.getInputStream();
// 将输入流中的数据写入到标准输出流中
PrintWriter out = response.getWriter();
int len = -1;
byte[] buffer = new byte[1024];
while ((len = is.read(buffer)) != -1) {
String str = new String(buffer, 0, len);
out.println(str);
}
// 关闭输入流
is.close();
}
}
文件上传到服务器端后对输入流的解析
通常使用第三方jar包完成,jar包下载,通过apache官网找到PROJECT LIST
commons->FileUpload
进入下载想要的版本
在说明中可以看到,还需要下载Commons IO 2.2(或以上版本都可以)
下载导入项目即可。
上传图片并在服务器另存
public class RegisterServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 判断请求是否为multipart请求
if (!ServletFileUpload.isMultipartContent(request)) {
throw new RuntimeException("当前请求不支持文件上传");
}
try {
// 创建一个FileItem工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
// 创建文件上传核心组件
ServletFileUpload upload = new ServletFileUpload(factory);
// 解析请求,获取到所有的items
List<FileItem> items = upload.parseRequest(request);
// 遍历item
for (FileItem item : items) {
// 判断item是普通表单项还是文件表单项
if (item.isFormField()) {
// 获取表单项名称,值
String fieldName = item.getFieldName();
String fieldValue = item.getString();
System.out.println(fieldName + "=" + fieldValue);
} else {
// 获取文件表单项文件名,内容
String fileName = item.getName();
InputStream is = item.getInputStream();
// 将文件另存
String path = this.getServletContext().getRealPath("/images");
File descFile = new File(path, fileName);
OutputStream os = new FileOutputStream(descFile);
int len = -1;
byte[] buffer = new byte[1024];
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
// 关闭流
os.close();
is.close();
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
}
成功
设置临时文件
客户端向服务器上传文件时,文件在上传时是被分割成了很多数据包,在网络中传输中路径是不同的,到达客户端的顺序是不确定的。此时服务器中就引入了临时文件对其进行接收和临时存储,在接收完之后一次性交给输入流处理。也可以避免文件过大占用太多内存。
上传大于1M的图片,可以在temp文件夹中找到生成的临时文件
删除临时文件
此时会在文件读取完之后删除临时文件。
解决普通表单项乱码问题
解决文件名相关问题
文件名乱码
相同文件名文件被替换
实现对文件大小的限制的
实现对上传文件目录的管理
这样每天的文件都会保存在单独的文件中。不会出现一个文件夹存放太多文件的现象。
如果还是不够就多建几层文件夹,一天的一个文件夹,放在月份里,月份的放年里。
文件上传最终版本
public class RegisterServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 判断请求是否为multipart请求
if (!ServletFileUpload.isMultipartContent(request)) {
throw new RuntimeException("当前请求不支持文件上传");
}
try {
// 创建一个FileItem工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
// 设置使用临时文件的边界值
// 大于该值上传文件会先保存在临时文件中,否则写入内存
factory.setSizeThreshold(1024 * 1024 * 1);// 字节
// 设置临时文件
String tempPath = this.getServletContext().getRealPath("/temp");
System.out.println(tempPath);
File temp = new File(tempPath);
factory.setRepository(temp);
// 创建文件上传核心组件
ServletFileUpload upload = new ServletFileUpload(factory);
// 设置每一个item的头部字符编码,解决文件名中文乱码
upload.setHeaderEncoding("UTF-8");
// 设置每个上传文件的最大边界值
upload.setFileSizeMax(1024 * 1024 * 2);
// 设置一次上传所有文件的总和的最大值
upload.setSizeMax(1024 * 1024 * 5);
// 解析请求,获取到所有的items
List<FileItem> items = upload.parseRequest(request);
// 遍历item
for (FileItem item : items) {
// 判断item是普通表单项还是文件表单项
if (item.isFormField()) {
// 获取表单项名称,值
String fieldName = item.getFieldName();
String fieldValue = item.getString("UTF-8");
System.out.println(fieldName + "=" + fieldValue);
} else {
// 获取文件表单项文件名,内容
String fileName = item.getName();// 原始文件名
fileName = System.currentTimeMillis() + fileName;
InputStream is = item.getInputStream();
// 将文件另存
String path = this.getServletContext().getRealPath("/images");
// 获取当前系统时间
Calendar now = Calendar.getInstance();
int year = now.get(Calendar.YEAR);
int month = now.get(Calendar.MONTH) + 1;
int day = now.get(Calendar.DAY_OF_MONTH);
path = path + "/" + year + "/" + month + "/" + day;
// 若该目录不存在则创建
File dirFile = new File(path);
if (!dirFile.exists())
dirFile.mkdirs();// 创建多级目录
File descFile = new File(path, fileName);
OutputStream os = new FileOutputStream(descFile);
int len = -1;
byte[] buffer = new byte[1024];
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
// 关闭流
os.close();
is.close();
// 删除临时文件
item.delete();
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
}
文件下载的实现
超链接方式的文件下载
下载内容的形式由浏览器决定。
对于浏览器能解析的,例如图片,视频等都会直接在浏览器上显示。
Servlet方式的文件下载
public class DownloadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 文件名
String fileName = "金币.jpg";
// 打散,按当前字符编码格式
byte[] bytes = fileName.getBytes("UTF-8");
// 组装,按目标字符编码
fileName = new String(bytes, "ISO8859-1");
// 修改相应的头部属性content-disposition值为attachment(附件)
response.setHeader("content-disposition", "attachment;filename=" + fileName);
// 获取连接服务器端资源文件的输入流
InputStream is = this.getServletContext().getResourceAsStream("/resources/gold.jpg");
// 获取输出流
ServletOutputStream os = response.getOutputStream();
// 将输入流中内容写入输出流
int len = -1;
byte[] buffer = new byte[1024];
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
// 关闭流
os.close();
is.close();
}
}