文件上传
文件上传指的是 通过 浏览器 向服务器 上传 某个文件,服务器 接收 到该文件 后 会 将 该文件 存储在 服务器的
硬盘中,通常 不会存在 数据库,这样可以 减轻数据库 的压力并且在文件的 操作上 更加灵活,常见的功能是上传头像图片。
文件上传的原理
所谓的 文件上传 就是服务器端通过 request对象 获取输入流,将浏览器 端上传的 数据读取 出来,保存到 服务器端。
文件上传的要求
- 提供form表单,表单 的提交方式 必须是post
- form表单中的 enctype 属性 必须是 mutipart/form-data
- 表单中 提供 input type="file"上传 输入域
创建一个upload.jsp的文件,里面提供上传的按钮:
<form action="${pageContext.request.contextPath }/upload5" method="post" enctype="multipart/form-data">
<input type="file" name="photo"><br>
<input type="submit" value="上传"><br>
</form>
创建Servlet用来处理用户上传的文件
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取 请求的 输入流
InputStream is = request.getInputStream();
//读取输入流中的数据
int len = 0;
byte[] b = new byte[1024];
while((len=is.read(b))!=-1) {
//打印文件的字节内容
System.out.println(new String(b,0,len));
}
}
FileUpload工具的使用
在实际开发中通常 会 借助第三方 工具实现上传功能,应用较多的是apache旗下sh的 Commons-fileupload。Apache开源组织提供 一个用来 处理表单文件上传的开源组件(Commons-fileupload),该组件性能优异,并且其API使用极其简单,可以让开发人员轻松实现Web文件上传功能,因此自web开发中实现文件上传功能,通常使用 Commons-fileupload组件实现。在使用 该工具 实现上传功能时,首先 需要导入Commons-fileuploads和commons-io两个jar包。
代码实例
**
* 使用common fileupload完成上传
*/
public class UploadServlet2 extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1、判断表单是否支持文件上传。 即: enctype = "multipart/form-data"
boolean isMultipartContent = ServletFileUpload.isMultipartContent(request);
if(!isMultipartContent) {
throw new RuntimeException("该请求不能完成文件上传");
}
//2、创建一个 DiskFileItemfactory对象
DiskFileItemFactory factory = new DiskFileItemFactory(); //factory
//3、创建一个ServletFileUpload对象,该对象是上传的核心组件
ServletFileUpload sfu = new ServletFileUpload(factory);
//4、解析request对象,并得到 一个表单项的 集合
try {
List<FileItem> fileItems = sfu.parseRequest(request);
//5、遍历该集合,集合中的每一项 即为 每一个表单项
for(FileItem item:fileItems) {
if(item.isFormField()) {
//普通表单项,不是文件项
//等价于以前的 request.getParameter()
String fieldname = item.getFieldName(); //字段名
String fieldvalue = item.getString("UTF-8"); //字段值
System.out.println(fieldname+"="+fieldvalue);
}else {
//上传表单项
//获取文件名
String fileName = item.getName();
//获取输入流
InputStream is = item.getInputStream();
//创建输出流
String path = this.getServletContext().getRealPath("/upload");
//获得 文件在服务器上上传的 路径
File file = new File(path,fileName);
FileOutputStream fos = new FileOutputStream(file);
//完成文件的复制
byte[] bytes = new byte[1024];
int len = -1;
//从客户端 读取数据 并将其 写到 服务器端的 硬盘上
while((len=is.read(bytes))!=-1) {
fos.write(bytes, 0, len);
}
}
}
} catch (FileUploadException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
解析原理:
临时目录
文件由浏览器通过网络上传到服务器,并不是直接通过一条网络线路将所有请求数据发送到了服务器的。而是将这些数据分为了很多个数据包,这些数据包分别被编号后,经由不同的网络线路最终发送到了服务器中。这些数据包到达服务器的时间会根据不同的网络线路的情况不同,分别先后到达服务器,顺序是不定的。因此服务器会在其临时目录中,创建一个临时文件,将这些数据包进行拼接组装。Tomcat 默认情况下的临时目录是 Tomcat 服务器安装目录的 temp 子目录。我们也可以修改临时目录的默认位置。Apache 的 FileUpload 支持设置创建临时文件的最小临界值,即只有上传的文件大小超出这个值,才会创建临时文件。通过 DiskFileItemFactory 的 setSizeThreshold()方法可以设置临界值,单位为字节。通过 DiskFileItemFactory 的 setRepository()方法可以指定临时目录。
需要注意的是将temp文件夹中的临时文件删除,这个需要在IO流关闭之后才能删除。
当上传的文件大小 超过 10kb时 就会 将文件 放到 临时目录中 了。10kb是默认值,可以通过DiskFileItemFactory中的setSizeThreshold方法修改:
factory.setSizeThreshold(102410243);
上面代码的作用是当上传文件大小超过3MB时 才会是用那个临时文件。
上传文件的大小
对于上传文件的大小,可以通过 ServletFileUpload的setFileSizeMax()与setSizeMax()方法进行控制。setFileSizeMax()用于设置单个文件上传的最大值,而setSizeMax()用于设置 单次上传的最大值,
创建目录
无论是 Windows 系统、Linux 系统,还是其它系统,其目录中所包含的文件数量是有上
限的。所以对于上传的文件,应该分目录进行管理。若文件较多,则可按照年、月、日创建多级子目
录。这样,即方便管理,又不会超出目录的文件数量上限。
//获取输入流
InputStream is = item.getInputStream();
//创建输出流
String path = this.getServletContext().getRealPath("/upload");
//获取当前系统时间的年月日
LocalDate now = LocalDate.now();
int year = now.getYear();
int month = now.getMonthValue();
int day = now.getDayOfMonth();
//在upload下分别创建年、月、日三级子目录
path = path + "/" + year + "/" + month + "/" +day;
//创建父目录
File parentDir = new File(path);
//如果父目录不存在,则创建
if(!parentDir.exists()){
parentDir.mkdirs();
}
文件下载
超链接下载
超链接下载是指,将下载 资源作为 超链接 的连接目的文件出现。若 浏览器 可以 解析 该资源文件,则将 在浏览器上 直接 显示文件内容;若浏览器不支持 文件的解析,则会 弹出 另 存为 对话框,要求 用户保存。
在WebContent文件夹下创建download文件夹,里面 放入一些文件:
创建jsp:
使用超链接下载文件<br>
<a href="${pageContext.request.contextPath }/download/buhuo.JPG">下载图片</a>
<a href="${pagecontext.request.contextPath }/download/MySQL常用命令.zip">下载压缩包</a>
<br>
使用servlet实现下载
若要 使得 下载 的文件 以附件的形式 出现在 浏览器中,则需要设置 响应头的属性 content-disposition的值为 attachment,且 需要 指定浏览器 下载 后显示的文件名。 即需要指定 content-disposition的值为attachment;filename=文件名。
package com.monkey1024.servlet;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 文件下载
*/
public class DownloadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置响应头属性值,使文件以附件形式进行下载
response.setHeader("content-disposition", "attachment;filename=monkey1024.png");
//获取输入流
InputStream is = this.getServletContext().getResourceAsStream("/download/monkey1024.png");
ServletOutputStream os = response.getOutputStream();
//复制文件
byte[] bytes = new byte[1024];
int len = 1;
while((len = is.read(bytes)) != -1){
os.write(bytes, 0, len);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
创建jsp:
解决文件名乱码问题:
//这里设置的是文件下载下来的名字
String fileName = "123.JPG";
fileName = new String(fileName.getBytes("utf-8"),"iso8859-1");
//设置 响应头属性,使文件 以附件形式下载
response.setHeader("content-disposition", "attachment;filename="+fileName);