一、实现Web开发中的文件上传功能,需完成如下两步操作:
1、在web页面中添加上传输入项。
2、在Servlet中读取上传文件的数据,并保存到本地硬盘中。
二、如何在web页面中添加上传输入项?
1、<input type="file">标签用于在web页面中添加文件上传输入项,设置文件上传输入项时须注意:
1)必须设置input输入项的name属性,否则浏览器将不会发送上传文件的数据。
2)必须把form的enctype属性值设为multipart/form-data。设置该值后,浏览器在上传文件时,将把文件数据附带在http请求消息体中,并使用MIME协议对上传的文件进行描述,以方便接收方对上传数据进行解析和处理。
三、在web开发中实现文件上传功能,通常使用commons-fileupload组件实现。使用commons-fileupload组件实现文件上传,需要导入该组件相应的支撑jar包:
1、commons-fileupload-1.3.1.jar
2、commons-io-2.4.jar
四、上传文件的细节:
1、中文乱码的问题
1)普通字段的中文乱码问题:FileItem.getString("UTF-8"),FileItem表示上传表单中的表单项内容。
2)上传字段的中文乱码问题:ServletUploadFile.setHeaderEncoding("UTF-8");
2、临时文件的删除问题
1)通过FileItem.delete()
2)一定要在关闭IO流之后
3、在同一个目录下上传相同文件名的问题
1)将文件名拼接一个唯一标识符,即UUID
4、单个目录下文件过多的问题
1)采用位运算解决单个目录文件过多
2)思路:
5、为安全将上传的文件放入客户端无法直接访问的目录中的问题
1)将上传的文件,放置到/WEB-INF/upload/目录下
6、重构思想
1)做到重用性
7、自定义封装上传文件工具类的问题
8、上传文件的大小问题
9、上传多个文件的问题
10、上传多个文件的界面问题
五、显示下载文件列表
1)递归方式查询可供下载的文件,一定要有出口条件。
2)使用Map<UUID文件名,真实文件名>收集可供下载的文件
3)使用<c:url>和<c:param>对中文名进行URL编码
六、下载文件
1、对传过来的中文编码进行URL解码
2、通过UUID文件名,反向查到该文件所在的真实目录
七、文件上传下载与数据库结合
1、在将上传文件保存的同时,写往数据库表,一个上传文件对应一条记录,确保uuidFileName双方一致。
八、上传和下载代码
1、文件目录图
2、UpDao.java
package com.gnnuit.web.dao; import java.sql.SQLException; import org.apache.commons.dbutils.QueryRunner; import com.gnnuit.web.domain.Up; import com.gnnuit.web.util.JdbcUtil; //向数据库添加up对象 public class UpDao { public void addUp(Up up) throws SQLException { QueryRunner qr = new QueryRunner(JdbcUtil.getDataSource()); String sql = "insert into up(username,realFileName,uuidFileName) values(?,?,?)"; qr.update(sql, new Object[] { up.getUsername(), up.getRealFileName(), up.getUuidFileName() }); } }
3、Up.java
package com.gnnuit.web.domain; public class Up { private int id; private String username; private String realFileName; private String uuidFileName; public Up() { } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getRealFileName() { return realFileName; } public void setRealFileName(String realFileName) { this.realFileName = realFileName; } public String getUuidFileName() { return uuidFileName; } public void setUuidFileName(String uuidFileName) { this.uuidFileName = uuidFileName; } }
4、User.java
package com.gnnuit.web.domain; import java.util.ArrayList; import java.util.List; import org.apache.commons.fileupload.FileItem; //封装上传文件的内容 public class User { // 上传用户 private String username; // 上传的文件 private List<FileItem> upfileList = new ArrayList<FileItem>(); public User() { } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public List<FileItem> getUpfileList() { return upfileList; } public void setUpfileList(List<FileItem> upfileList) { this.upfileList = upfileList; } }
5、NoUpfileException.java
package com.gnnuit.web.exception; public class NoUpfileException extends Exception{ private static final long serialVersionUID = 1L; }
6、UpfileSizeException.java
package com.gnnuit.web.exception; public class UpfileSizeException extends Exception{ private static final long serialVersionUID = 1L; }
7、UpfileTypeException.java
package com.gnnuit.web.exception; public class UpfileTypeException extends Exception{ private static final long serialVersionUID = 1L; }
8、DownLoadServlet.java
package com.gnnuit.web.servlet.download; 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; import com.gnnuit.web.util.UploadUtil; public class DownLoadServlet extends HttpServlet { private static final long serialVersionUID = 1L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取传过来的UUID文件名,参数带有中文,需要做处理 String uuidFileName = request.getParameter("fileName"); byte[] buf = uuidFileName.getBytes("ISO8859-1"); uuidFileName = new String(buf, "UTF-8"); // 根据UUID文件名获取真实文件名 int index = uuidFileName.lastIndexOf("_"); String realFileName = uuidFileName.substring(index + 1); // 设置弹出下载框 String uploadPath = this.getServletContext().getRealPath( UploadUtil.uploadPath); String uuidFilePath = UploadUtil.makeUuidFilePath(uploadPath, uuidFileName); response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(realFileName, "UTF-8")); // 开始下载 FileInputStream fis = new FileInputStream(new File(uuidFilePath + "/" + uuidFileName)); OutputStream out = response.getOutputStream(); int len = 0; buf = new byte[1024]; while ((len = fis.read(buf)) > 0) { out.write(buf, 0, len); } fis.close(); out.close(); } }
9、FileListServlet.java
package com.gnnuit.web.servlet.download; 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; import com.gnnuit.web.util.UploadUtil; public class FileListServlet extends HttpServlet { private static final long serialVersionUID = 1L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String uploadPath = this.getServletContext().getRealPath( UploadUtil.uploadPath); Map<String, String> map = new HashMap<>();// key代表UUID文件名,value代表真实文件名 getFiles(uploadPath, map); request.setAttribute("map", map); request.getRequestDispatcher("/download.jsp") .forward(request, response); } // 获取下载目录的所有文件 private void getFiles(String uploadPath, Map<String, String> map) { File file = new File(uploadPath); if (file.isFile()) { // 是文件,不是目录 String uuidFileName = file.getName(); int index = uuidFileName.lastIndexOf("_"); String realFileName = uuidFileName.substring(index + 1); map.put(uuidFileName, realFileName); } else { // 是目录 File[] files = file.listFiles(); for (File f : files) { getFiles(f.getPath(), map); } } } }
10、UploadServlet.java
package com.gnnuit.web.servlet.upload; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.gnnuit.web.dao.UpDao; import com.gnnuit.web.domain.Up; import com.gnnuit.web.domain.User; import com.gnnuit.web.exception.NoUpfileException; import com.gnnuit.web.exception.UpfileSizeException; import com.gnnuit.web.exception.UpfileTypeException; import com.gnnuit.web.util.UploadUtil; public class UploadServlet extends HttpServlet { private static final long serialVersionUID = 1L; public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { User user = UploadUtil.doUpload(request); String uploadPath = this.getServletContext().getRealPath( UploadUtil.uploadPath); List<Up> upList = new ArrayList<>(); // 写入硬盘 UploadUtil.doSave(user, uploadPath, upList); //写入数据库 UpDao dao = new UpDao(); for (Up up : upList) { dao.addUp(up); } request.setAttribute("message", "上传文件成功"); request.getRequestDispatcher("/WEB-INF/message.jsp").forward( request, response); } catch (UpfileSizeException e) { e.printStackTrace(); request.setAttribute("message", "上传文件大小限制在200K以内"); request.getRequestDispatcher("/WEB-INF/message.jsp").forward( request, response); } catch (UpfileTypeException e) { e.printStackTrace(); request.setAttribute("message", "只能上传JPG格式的文件"); request.getRequestDispatcher("/WEB-INF/message.jsp").forward( request, response); } catch (NoUpfileException e) { e.printStackTrace(); request.setAttribute("message", "无上传文件"); request.getRequestDispatcher("/WEB-INF/message.jsp").forward( request, response); } catch (Exception e) { e.printStackTrace(); request.setAttribute("message", "上传文件失败"); request.getRequestDispatcher("/WEB-INF/message.jsp").forward( request, response); } } }
11、up.sql
use mydb3; drop table if exists up; create table if not exists up( id int primary key auto_increment, username varchar(20) not null, realFileName varchar(20) not null, uuidFileName varchar(100) not null );
12、JdbcUtil.java
package com.gnnuit.web.util; import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; public final class JdbcUtil { private static ComboPooledDataSource cpds; static { cpds = new ComboPooledDataSource(); } // 获取数据源 public static DataSource getDataSource() { return cpds; } // 取得连接 public static Connection getMySqlConnection() throws SQLException { return cpds.getConnection(); } // 关闭连接 public static void close(Connection conn) throws SQLException { if (conn != null) { conn.close(); } } }
13、UploadUtil.java
package com.gnnuit.web.util; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; import java.util.UUID; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import com.gnnuit.web.domain.Up; import com.gnnuit.web.domain.User; import com.gnnuit.web.exception.NoUpfileException; import com.gnnuit.web.exception.UpfileSizeException; public final class UploadUtil { // 取得上传使用的临时和真实目录 public static final String tempPath = "/WEB-INF/temp"; public static final String uploadPath = "/WEB-INF/upload"; // 取得真实文件名 public static String getRealFileName(String realFileName) { int index = realFileName.lastIndexOf("\\"); if (index >= 0) { // IE6浏览器 realFileName = realFileName.substring(index + 1); } return realFileName; } // 取得uuid文件名 public static String makeUuidFilePath(String uploadPath, String uuidFileName) { String uuidFilePath = null; int code = uuidFileName.hashCode();// 8 int dir1 = code & 0xF;// 3 int dir2 = code >> 4 & 0xF;// A File file = new File(uploadPath + "/" + dir1 + "/" + dir2); // 如果该目录未存在 if (!file.exists()) { // 一次性创建N层目录 file.mkdirs(); } uuidFilePath = file.getPath(); return uuidFilePath; } // 取得upload/目录下的分散目录 public static String makeUuidFileName(String realFileName) { return UUID.randomUUID().toString() + "_" + realFileName; } // 文件复制 public static void doSave(InputStream is, String uuidFileName, String uuidFilePath) { OutputStream os = null; try { os = new FileOutputStream(uuidFilePath + "/" + uuidFileName); byte[] buf = new byte[1024]; int len = 0; while ((len = is.read(buf)) > 0) { os.write(buf, 0, len); } } catch (Exception e) { e.printStackTrace(); } finally { if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if (os != null) { try { os.close(); } catch (IOException e) { e.printStackTrace(); } } } } // 将上传文件封装成JavaBean对象中 public static User doUpload(HttpServletRequest request) throws Exception { User user = new User(); // 创建上传文件工厂 DiskFileItemFactory factory = new DiskFileItemFactory(); // 设置内存中缓存区的大小,默认10K factory.setSizeThreshold(100 * 1024); // 设置上传文件临时存放的目录 String tempPath = request.getSession().getServletContext() .getRealPath(UploadUtil.tempPath); factory.setRepository(new File(tempPath)); // 创建上传文件对象[核心] ServletFileUpload upload = new ServletFileUpload(factory); // 设置上传文件的中文编码方式 upload.setHeaderEncoding("UTF-8"); // 客户端上传文件是否使用MIME协议, boolean flag = ServletFileUpload.isMultipartContent(request); if (!flag) { // 不是以MIME协议上传文件 throw new ServletException(); } else { /* * 是以MIME协议上传的文件,解析request中的所有上传内容每个内容封装成一个对象FileItem, * FileItem代表普通字段和上传字段二类 */ List<FileItem> fileItemList = upload.parseRequest(request); for (FileItem fileItem : fileItemList) { if (fileItem.isFormField()) { String fieldValue = fileItem.getString("UTF-8"); user.setUsername(fieldValue); } else { // 必定是上传字段 // 如果无上传文件 if (fileItem.getSize() == 0) { throw new NoUpfileException(); } // 只能上传JPG文件 /*String realFileName = UploadUtil.getRealFileName(fileItem .getName()); if (!realFileName.endsWith("jpg")) { throw new UpfileTypeException(); }*/ // 只有上传<=200K的文件 if (fileItem.getSize() > 200 * 1024) { throw new UpfileSizeException(); } // 封装到JavaBean user.getUpfileList().add(fileItem); } }// end of for loop } return user; } public static void doSave(User user, String uploadPath, List<Up> upList) throws Exception { // 取得该用户上传的所有文件集合 List<FileItem> fileItemList = user.getUpfileList(); // 迭代每个文件,并上传 for (FileItem fileItem : fileItemList) { // 创建Up对象 Up up = new Up(); up.setUsername(user.getUsername()); // 取得输入流 InputStream is = fileItem.getInputStream(); // 取得真实文件名 String realFileName = fileItem.getName(); realFileName = UploadUtil.getRealFileName(realFileName); // 取得UUID文件名 String uuidFileName = UploadUtil.makeUuidFileName(realFileName); // 取得UUID文件路径 String uuidFilePath = UploadUtil.makeUuidFilePath(uploadPath, uuidFileName); // 保存 UploadUtil.doSave(is, uuidFileName, uuidFilePath); // 收集Up信息 up.setUuidFileName(uuidFileName); up.setRealFileName(realFileName); upList.add(up); // 删除临时文件 fileItem.delete(); } } }
14、c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <default-config> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="user">root</property> <property name="password">root</property> <property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/mydb3</property> </default-config> </c3p0-config>
15、message.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <body> ${message } </body> </html>
16、download.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <body> <table align="center" border="1"> <tr> <th>文件列表</th> <th>操作</th> </tr> <c:forEach var="entry" items="${map }"> <tr> <td>${entry.value }</td> <td> <c:url var="myURL" value="/DownLoadServlet"> <c:param name="fileName" value="${entry.key }"></c:param> </c:url> <a href="${myURL }" >下载</a> </td> </tr> </c:forEach> </table> </body> </html>
17、upload.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <script type="text/javascript"> var time=0; function addDiv(addButton) { var outDiv=document.getElementById("outDiv"); var divElement= document.createElement("div"); var inputElement=document.createElement("input"); inputElement.type="file"; inputElement.name="filename"; var inputElement2=document.createElement("input"); inputElement2.type="button"; inputElement2.value="删除"; inputElement2.onclick=function(){ var parentNode= this.parentNode.parentNode; parentNode.removeChild(this.parentNode); time--; if(time<5){ addButton.disabled=false; } }; divElement.appendChild(inputElement); divElement.appendChild(inputElement2); outDiv.appendChild(divElement); time++; if(time==5){ addButton.disabled=true; } } </script> </head> <body> <form action="${pageContext.request.contextPath }/UploadServlet" method="post" enctype="multipart/form-data"> <table border="1" align="center"> <tr> <td>上传用户</td> <td><input type="text" name="username"/></td> </tr> <tr> <td>上传文件</td> <td> <div id="outDiv"> </div> <%--<input type="file" name="filename"/> --%> <input type="button" value="添加" onclick="addDiv(this)"/> </td> </tr> <tr> <td colspan="2" align="center"> <input type="submit" value="提交"/> </td> </tr> <tr> <td colspan="2" align="center"> <a href="${pageContext.request.contextPath }/FileListServlet">显示下载文件列表</a> </td> </tr> </table> </form> </body> </html>