在Web应用系统开发中,文件上传和下载功能是非常常用的功能,今天来讲一下JavaWeb中的文件上传和下载功能的实现。
对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的,如果直接使用Servlet获取上传文件的输入流然后再解析里面的请求参数是比较麻烦,所以一般选择采用apache的开源工具common-fileupload这个文件上传组件。这个common-fileupload上传组件的jar包可以去apache官网上面下载,也可以在struts的lib文件夹下面找到,struts上传的功能就是基于这个实现的。common-fileupload是依赖于common-io这个包的,所以还需要下载这个包。
一、开发环境搭建
创建一个FileUploadAndDownLoad项目,加入Apache的commons-fileupload文件上传组件的相关Jar包(注意要放到WebRoot--WEB-INF--lib下,而不是build path),如下图所示:

二、实现文件上传
2.1、文件上传页面和消息提示页面
upload.jsp页面的代码如下:
- <%@ page language="java" pageEncoding="UTF-8"%>
- >
- <html>
- <head>
- <title>文件上传title>
- head>
-
- <body>
- <form action="${pageContext.request.contextPath}/servlet/UploadHandleServlet" enctype="multipart/form-data" method="post">
- 上传用户:<input type="text" name="username"><br/>
- 上传文件1:<input type="file" name="file1"><br/>
- 上传文件2:<input type="file" name="file2"><br/>
- <input type="submit" value="提交">
- form>
- body>
- html>
message.jsp的代码如下:
- <%@ page language="java" pageEncoding="UTF-8"%>
- >
- <html>
- <head>
- <title>消息提示title>
- head>
-
- <body>
- ${message}
- body>
- html>
2.2、处理文件上传的Servlet
UploadHandleServlet的代码如下:
- package me.gacl.web.controller;
-
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.util.List;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.apache.commons.fileupload.FileItem;
- import org.apache.commons.fileupload.disk.DiskFileItemFactory;
- import org.apache.commons.fileupload.servlet.ServletFileUpload;
-
- public class UploadHandleServlet extends HttpServlet {
-
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
-
- String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");
- File file = new File(savePath);
-
- if (!file.exists() && !file.isDirectory()) {
- System.out.println(savePath+"目录不存在,需要创建");
-
- file.mkdir();
- }
-
- String message = "";
- try{
-
-
- DiskFileItemFactory factory = new DiskFileItemFactory();
-
- ServletFileUpload upload = new ServletFileUpload(factory);
-
- upload.setHeaderEncoding("UTF-8");
-
- if(!ServletFileUpload.isMultipartContent(request)){
-
- return;
- }
-
- List list = upload.parseRequest(request);
- for(FileItem item : list){
-
- if(item.isFormField()){
- String name = item.getFieldName();
-
- String value = item.getString("UTF-8");
-
- System.out.println(name + "=" + value);
- }else{
-
- String filename = item.getName();
- System.out.println(filename);
- if(filename==null || filename.trim().equals("")){
- continue;
- }
-
-
- filename = filename.substring(filename.lastIndexOf("\\")+1);
-
- InputStream in = item.getInputStream();
-
- FileOutputStream out = new FileOutputStream(savePath + "\\" + filename);
-
- byte buffer[] = new byte[1024];
-
- int len = 0;
-
- while((len=in.read(buffer))>0){
-
- out.write(buffer, 0, len);
- }
-
- in.close();
-
- out.close();
-
- item.delete();
- message = "文件上传成功!";
- }
- }
- }catch (Exception e) {
- message= "文件上传失败!";
- e.printStackTrace();
-
- }
- request.setAttribute("message",message);
- request.getRequestDispatcher("/message.jsp").forward(request, response);
- }
-
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
-
- doGet(request, response);
- }
- }
在Web.xml文件中注册UploadHandleServlet
- <servlet>
- <servlet-name>UploadHandleServletservlet-name>
- <servlet-class>me.gacl.web.controller.UploadHandleServletservlet-class>
- servlet>
-
- <servlet-mapping>
- <servlet-name>UploadHandleServletservlet-name>
- <url-pattern>/servlet/UploadHandleServleturl-pattern>
- servlet-mapping>
运行效果如下:

文件上传成功之后,上传的文件保存在了WEB-INF目录下的upload目录,如下图所示:

2.3、文件上传的细节
上述的代码虽然可以成功将文件上传到服务器上面的指定目录当中,但是文件上传功能有许多需要注意的小细节问题,以下列出的几点需要特别注意的
1、为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于WEB-INF目录下。
2、为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名。
3、为防止一个目录下面出现太多文件,要使用hash算法打散存储。
4、要限制上传文件的最大值。
5、要限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法。
针对上述提出的5点细节问题,我们来改进一下UploadHandleServlet,改进后的代码如下:
- package me.gacl.web.controller;
-
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.util.List;
- import java.util.UUID;
-
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.apache.commons.fileupload.FileItem;
- import org.apache.commons.fileupload.FileUploadBase;
- import org.apache.commons.fileupload.ProgressListener;
- import org.apache.commons.fileupload.disk.DiskFileItemFactory;
- import org.apache.commons.fileupload.servlet.ServletFileUpload;
-
-
-
-
-
-
-
-
- public class UploadHandleServlet extends HttpServlet {
-
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
-
- String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");
-
- String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp");
- File tmpFile = new File(tempPath);
- if (!tmpFile.exists()) {
-
- tmpFile.mkdir();
- }
-
-
- String message = "";
- try{
-
-
- DiskFileItemFactory factory = new DiskFileItemFactory();
-
- factory.setSizeThreshold(1024*100);
-
- factory.setRepository(tmpFile);
-
- ServletFileUpload upload = new ServletFileUpload(factory);
-
- upload.setProgressListener(new ProgressListener(){
- public void update(long pBytesRead, long pContentLength, int arg2) {
- System.out.println("文件大小为:" + pContentLength + ",当前已处理:" + pBytesRead);
-
-
-
-
-
-
- }
- });
-
- upload.setHeaderEncoding("UTF-8");
-
- if(!ServletFileUpload.isMultipartContent(request)){
-
- return;
- }
-
-
- upload.setFileSizeMax(1024*1024);
-
- upload.setSizeMax(1024*1024*10);
-
- List list = upload.parseRequest(request);
- for(FileItem item : list){
-
- if(item.isFormField()){
- String name = item.getFieldName();
-
- String value = item.getString("UTF-8");
-
- System.out.println(name + "=" + value);
- }else{
-
- String filename = item.getName();
- System.out.println(filename);
- if(filename==null || filename.trim().equals("")){
- continue;
- }
-
-
- filename = filename.substring(filename.lastIndexOf("\\")+1);
-
- String fileExtName = filename.substring(filename.lastIndexOf(".")+1);
-
- System.out.println("上传的文件的扩展名是:"+fileExtName);
-
- InputStream in = item.getInputStream();
-
- String saveFilename = makeFileName(filename);
-
- String realSavePath = makePath(saveFilename, savePath);
-
- FileOutputStream out = new FileOutputStream(realSavePath + "\\" + saveFilename);
-
- byte buffer[] = new byte[1024];
-
- int len = 0;
-
- while((len=in.read(buffer))>0){
-
- out.write(buffer, 0, len);
- }
-
- in.close();
-
- out.close();
-
-
- message = "文件上传成功!";
- }
- }
- }catch (FileUploadBase.FileSizeLimitExceededException e) {
- e.printStackTrace();
- request.setAttribute("message", "单个文件超出最大值!!!");
- request.getRequestDispatcher("/message.jsp").forward(request, response);
- return;
- }catch (FileUploadBase.SizeLimitExceededException e) {
- e.printStackTrace();
- request.setAttribute("message", "上传文件的总的大小超出限制的最大值!!!");
- request.getRequestDispatcher("/message.jsp").forward(request, response);
- return;
- }catch (Exception e) {
- message= "文件上传失败!";
- e.printStackTrace();
- }
- request.setAttribute("message",message);
- request.getRequestDispatcher("/message.jsp").forward(request, response);
- }
-
-
-
-
-
-
-
-
- private String makeFileName(String filename){
-
- return UUID.randomUUID().toString() + "_" + filename;
- }
-
-
-
-
-
-
-
-
-
-
-
- private String makePath(String filename,String savePath){
-
- int hashcode = filename.hashCode();
- int dir1 = hashcode&0xf;
- int dir2 = (hashcode&0xf0)>>4;
-
- String dir = savePath + "\\" + dir1 + "\\" + dir2;
-
- File file = new File(dir);
-
- if(!file.exists()){
-
- file.mkdirs();
- }
- return dir;
- }
-
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
-
- doGet(request, response);
- }
- }
三、文件下载
3.1、列出提供下载的文件资源
我们要将Web应用系统中的文件资源提供给用户进行下载,首先我们要有一个页面列出上传文件目录下的所有文件,当用户点击文件下载超链接时就进行下载操作,编写一个ListFileServlet,用于列出Web应用系统中所有下载文件。
ListFileServlet的代码如下:
- package me.gacl.web.controller;
-
- import java.io.File;
- import java.io.IOException;
- import java.util.HashMap;
- import java.util.Map;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
-
-
-
-
-
-
-
-
- public class ListFileServlet extends HttpServlet {
-
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
-
- String uploadFilePath = this.getServletContext().getRealPath("/WEB-INF/upload");
-
- Map fileNameMap = new HashMap();
-
- listfile(new File(uploadFilePath),fileNameMap);
-
- request.setAttribute("fileNameMap", fileNameMap);
- request.getRequestDispatcher("/listfile.jsp").forward(request, response);
- }
-
-
-
-
-
-
-
-
- public void listfile(File file,Map map){
-
- if(!file.isFile()){
-
- File files[] = file.listFiles();
-
- for(File f : files){
-
- listfile(f,map);
- }
- }else{
-
-
-
-
-
- String realName = file.getName().substring(file.getName().indexOf("_")+1);
-
- map.put(file.getName(), realName);
- }
- }
-
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- doGet(request, response);
- }
- }
这里简单说一下ListFileServlet中listfile方法,listfile方法是用来列出目录下的所有文件的,listfile方法内部用到了递归,在实际开发当中,我们肯定会在数据库创建一张表,里面会存储上传的文件名以及文件的具体存放目录,我们通过查询表就可以知道文件的具体存放目录,是不需要用到递归操作的,这个例子是因为没有使用数据库存储上传的文件名和文件的具体存放位置,而上传文件的存放位置又使用了散列算法打散存放,所以需要用到递归,在递归时,将获取到的文件名存放到从外面传递到listfile方法里面的Map集合当中,这样就可以保证所有的文件都存放在同一个Map集合当中。
在Web.xml文件中配置ListFileServlet
- <servlet>
- <servlet-name>ListFileServletservlet-name>
- <servlet-class>me.gacl.web.controller.ListFileServletservlet-class>
- servlet>
-
- <servlet-mapping>
- <servlet-name>ListFileServletservlet-name>
- <url-pattern>/servlet/ListFileServleturl-pattern>
- servlet-mapping>
展示下载文件的listfile.jsp页面如下:
- <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
- <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
- >
- <html>
- <head>
- <title>下载文件显示页面title>
- head>
-
- <body>
-
- <c:forEach var="me" items="${fileNameMap}">
- <c:url value="/servlet/DownLoadServlet" var="downurl">
- <c:param name="filename" value="${me.key}">c:param>
- c:url>
- ${me.value}<a href="${downurl}">下载a>
- <br/>
- c:forEach>
- body>
- html>
访问ListFileServlet,就可以在listfile.jsp页面中显示提供给用户下载的文件资源,如下图所示:

3.2、实现文件下载
编写一个用于处理文件下载的Servlet,DownLoadServlet的代码如下:
- package me.gacl.web.controller;
-
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.io.OutputStream;
- import java.net.URLEncoder;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
-
- public class DownLoadServlet extends HttpServlet {
-
-
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
-
- String fileName = request.getParameter("filename");
- fileName = new String(fileName.getBytes("iso8859-1"),"UTF-8");
-
- String fileSaveRootPath=this.getServletContext().getRealPath("/WEB-INF/upload");
-
- String path = findFileSavePathByFileName(fileName,fileSaveRootPath);
-
- File file = new File(path + "\\" + fileName);
-
- if(!file.exists()){
- request.setAttribute("message", "您要下载的资源已被删除!!");
- request.getRequestDispatcher("/message.jsp").forward(request, response);
- return;
- }
-
- String realname = fileName.substring(fileName.indexOf("_")+1);
-
- response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(realname, "UTF-8"));
-
- FileInputStream in = new FileInputStream(path + "\\" + fileName);
-
- OutputStream out = response.getOutputStream();
-
- byte buffer[] = new byte[1024];
- int len = 0;
-
- while((len=in.read(buffer))>0){
-
- out.write(buffer, 0, len);
- }
-
- in.close();
-
- out.close();
- }
-
-
-
-
-
-
-
-
-
- public String findFileSavePathByFileName(String filename,String saveRootPath){
- int hashcode = filename.hashCode();
- int dir1 = hashcode&0xf;
- int dir2 = (hashcode&0xf0)>>4;
- String dir = saveRootPath + "\\" + dir1 + "\\" + dir2;
- File file = new File(dir);
- if(!file.exists()){
-
- file.mkdirs();
- }
- return dir;
- }
-
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- doGet(request, response);
- }
- }
在Web.xml文件中配置DownLoadServlet
- <servlet>
- <servlet-name>DownLoadServletservlet-name>
- <servlet-class>me.gacl.web.controller.DownLoadServletservlet-class>
- servlet>
-
- <servlet-mapping>
- <servlet-name>DownLoadServletservlet-name>
- <url-pattern>/servlet/DownLoadServleturl-pattern>
- servlet-mapping>
点击【下载】超链接,将请求提交到DownLoadServlet就行处理就可以实现文件下载了,运行效果如下图所示:

从运行结果可以看到,我们的文件下载功能已经可以正常下载文件了。
关于JavaWeb中的文件上传和下载功能的内容就这么多。
(本文原创:http://www.cnblogs.com/xdp-gacl/p/4200090.html)
本文转自http://blog.csdn.net/yxyx1024/article/details/44852587