时间:2021-07-01 10:21:17 帮助过:4人阅读
package cn.itcast.web.servlet.upload;
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.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 UploadServlet2 extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
//取得上传使用的临时和真实目录
String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp");
String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
//创建上传文件工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//设置内存中缓存区的大小,默认10K
factory.setSizeThreshold(100*1024);
//设置上传文件临时存放的目录
factory.setRepository(new File(tempPath));
//创建上传文件对象[核心]
ServletFileUpload upload = new ServletFileUpload(factory);
//设置上传文件的中文编码方式
upload.setHeaderEncoding("UTF-8");
//客户端上传文件是否使用MIME协议,
boolean flag = upload.isMultipartContent(request);
if(!flag){
//不是以MIME协议上传文件
throw new ServletException();
}else{
/*是以MIME协议上传的文件,解析request中的所有上传内容
*每个内容封装成一个对象FileItem,
*FileItem代表普通字段和上传字段二类
*/
try {
List<FileItem> fileItemList = upload.parseRequest(request);
for(FileItem fileItem : fileItemList){
if(fileItem.isFormField()){
//必定是普通字段
String fieldName = fileItem.getFieldName();
String fieldValue = fileItem.getString("UTF-8");
System.out.println(fieldName+":"+fieldValue);
}else {
//必定是上传字段
String realFileName = fileItem.getName();
int index = realFileName.lastIndexOf("\\");
if(index>=0){
//IE6浏览器
realFileName = realFileName.substring(index+1);
}
//通过真实文件名换算出唯一个文件名
String uuidFileName = makeUuidFileName(realFileName);
//通过位运算换算出upload目录下的子目录
String uuidFilePath = makeUuidFilePath(uploadPath,uuidFileName);
//取得文件输入流
InputStream is = fileItem.getInputStream();
//取得文件输出流
OutputStream os = new FileOutputStream(uuidFilePath+"/"+uuidFileName);
byte[] buf = new byte[1024];
int len = 0;
while( (len=is.read(buf))>0 ){
os.write(buf,0,len);
}
is.close();
os.close();
//将上传文件产生的临时文件删除
fileItem.delete();
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);
}
}
/*方式二
InputStream is = request.getInputStream();
OutputStream os = response.getOutputStream();
byte[] buf = new byte[1024];
int len = 0;
while( (len=is.read(buf))>0 ){
os.write(buf,0,len);
}
is.close();
os.close();
*/
/*方式一
String username = request.getParameter("username");
String upfile = request.getParameter("upfile");
System.out.println("上传用户:" + username);
System.out.println("上传文件:" + upfile);
*/
}
/*
* uploadPath="/upload"
* uuidFileName="fdafdsfdsa_a.JPG"
* return /upload/8/a 比较难,没看懂
*/
private 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;
}
/*
* realFileName="a.JPG"
* return "fdafdsfdsa_a.JPG"
*/
private String makeUuidFileName(String realFileName) {
return UUID.randomUUID().toString()+"_"+realFileName;
}
}
*2 上传文件的细节
(1)中文乱码的问题
a)普通字段的中文乱码问题:FileItem.getString("UTF-8"),
注意:FileItem表示上传表单中的表单项内容
b)上传字段的中文乱码问题:ServletUploadFile.setHeaderEncoding("UTF-8");
(2)临时文件的删除的问题
a)通过FileItem.delete()
b)一定要在关闭IO流之后
(3)在同一个目录下上传相同文件名的问题
a)将文件名拼接一个唯一标识符,即UUID
(4)单个目录下文件过多的问题
a)采用位运算解决单个目录文件过多
b)思路:参见<<>>
(5)为安全将上传的文件放入客户端无法直接访问的目录中的问题
a)将上传的文件,放置到/WEB-INF/upload/目录下
(6)重构思想
a)做到通用性
package cn.itcast.web.servlet.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 cn.itcast.web.servlet.domain.Up;
import cn.itcast.web.servlet.domain.User;
import cn.itcast.web.servlet.exception.NoUpfileException;
import cn.itcast.web.servlet.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 = upload.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 fieldName = fileItem.getFieldName();
String fieldValue = fileItem.getString("UTF-8");
user.setUsername(fieldValue);
}else {
//必定是上传字段
//如果无上传文件
if(fileItem.getSize()==0){
throw new NoUpfileException();
}
String realFileName = UploadUtil.getRealFileName(fileItem.getName());
/*只能上传JPG文件
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();
}
}
}
package cn.itcast.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 cn.itcast.web.servlet.domain.Up;
import cn.itcast.web.servlet.domain.User;
import cn.itcast.web.servlet.exception.NoUpfileException;
import cn.itcast.web.servlet.exception.UpfileSizeException;
import cn.itcast.web.servlet.exception.UpfileTypeException;
import cn.itcast.web.servlet.service.UpService;
import cn.itcast.web.servlet.util.UploadUtil;
public class UploadServlet extends HttpServlet {
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<Up>();
//写入硬盘
UploadUtil.doSave(user,uploadPath,upList);
//写入数据库表
UpService upService = new UpService();
upService.addUps(upList);
request.setAttribute("message","上传文件成功");
request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response);
}catch(UpfileSizeException e){
e.printStackTrace();
request.setAttribute("message","<font color=‘green‘>上传文件大小限制在200K以内</font>");
request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response);
}catch(UpfileTypeException e){
e.printStackTrace();
request.setAttribute("message","<font color=‘red‘>只能上传JPG格式的文件</font>");
request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response);
}catch(NoUpfileException e){
e.printStackTrace();
request.setAttribute("message","<font color=‘blue‘>无上传文件</font>");
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);
}
}
}
package cn.itcast.web.servlet.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;
}
}
package cn.itcast.web.servlet.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;
}
}
(7)自定义封装上传文件工具类的问题
课堂练习:无上传文件提交/只能上传JPG或jpg文件
(8)上传文件的大小的问题
(9)上传多个文件的问题
(10)上传多个文件的界面问题
Tip:多个文件上传的javascript编码
技巧:
•每次动态增加一个文件上传输入框,都把它和删除按纽放置在一个单独的div中,并对删除按纽的onclick事件进行响应,使之删除删除按纽所在的div。
•如:this.parentNode.parentNode.removeChild(this.parentNode);
*3 显示下载文件列表
a)递归方式查询可供下载的文件,一定要有出口条件
b)使用Map<UUID文件名,真实文件名>收集可供下载的文件
c)使用<c:url>和<c:param>对中文名进行URL编码
*4 下载文件
a)对传过来的中文编码进行URL解码
b)通过UUID文件名,反向查到该文件所在的真实目录
5 文件上传下载与数据库结合
a)在将上传文件保存的同时,写往数据库表,一个上传文件对应一条记录,确保uuidFileName双方一致
思考:
a)上传时,先硬盘,再表?
b)下载时,先硬盘,再表?
c)删除时,先硬盘,再表?
d)是否需要事务支持?
Tip:文件下载
Web应用中实现文件下载的两种方式
•超链接直接指向下载资源
•程序实现下载需设置两个响应头:
•设置Content-Type 的值为:application/x-msdownload。Web 服务器需要告诉浏览器其所输出的内容的类型不是普通的文本文件或 HTML 文件,而是一个要保存到本地的下载文件。
•Web 服务器希望浏览器不直接处理相应的实体内容,而是由用户选择将相应的实体内容保存到一个文件中,这需要设置 Content-Disposition 报头。该报头指定了接收程序处理数据内容的方式,在 HTTP 应用中只有 attachment 是标准方式,attachment 表示要求用户干预。在 attachment 后面还可以指定 filename 参数,该参数是服务器建议浏览器将实体内容保存到文件中的文件名称。在设置 Content-Dispostion 之前一定要指定 Content-Type.
•因为要下载的文件可以是各种类型的文件,所以要将文件传送给客户端,其相应内容应该被当做二进制来处理,所以应该调用 方法返回 ServletOutputStream 对象来向客户端写入文件内容。
package cn.itcast.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 cn.itcast.web.servlet.util.UploadUtil;
//显示可供下载的文件列表
public class ListFileServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
//定位下载文件的目录
String uploadPath = this.getServletContext().getRealPath(UploadUtil.uploadPath);
//创建Map<UUID文件名,真实文件名>
Map<String,String> map = new HashMap<String,String>();
//取得下载文件的相关信息
getFiles(uploadPath,map);
//转发到list.jsp显示可供下载的文件
request.setAttribute("map",map);
request.getRequestDispatcher("/WEB-INF/list.jsp").forward(request,response);
}
//递归询查所有可供下载的文件
private void getFiles(String uploadPath , Map<String,String> map){
File file = new File(uploadPath);
//如果file表示文件
if(file.isFile()){//出口
//取得文件名,即UUID文件名
String uuidFileName = file.getName();
int index = uuidFileName.indexOf("_");
String realFileName = uuidFileName.substring(index+1);
//存放到Map集合中
map.put(uuidFileName,realFileName);
}else{
//必定是目录
//取得该目录下的所有内容
File[] files = file.listFiles();
for(File f : files){
//递归调用自已
getFiles(f.getPath(),map);
}
}
}
}
<%@ page language="java" 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 border="1" align="center">
<caption>下载文件列表</caption>
<tr>
<th>文件名</th>
<th>操作</th>
</tr>
<c:forEach var="entry" items="${requestScope.map}">
<tr>
<td>${entry.value}</td>
<td>
<c:url var="myURL" value="/DownloadServlet">
<c:param name="uuidFileName" value="${entry.key}"/>
</c:url>
<a href="${myURL}"
style="text-decoration:none">
下载
</a>
</td>
</tr>
</c:forEach>
</table>
</body>
</html>
package cn.itcast.web.servlet.download;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
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 cn.itcast.web.servlet.util.UploadUtil;
//下载文件到本地
public class DownloadServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
String uuidFileName = request.getParameter("uuidFileName");
byte[] buf = uuidFileName.getBytes("ISO8859-1");
uuidFileName = new String(buf,"UTF-8");
int index = uuidFileName.lastIndexOf("_");
String realFileName = uuidFileName.substring(index+1);
response.setHeader("content-disposition","attachment;filename="+URLEncoder.encode(realFileName,"UTF-8"));
String uploadPath = this.getServletContext().getRealPath(UploadUtil.uploadPath);
String uuidFilePath = UploadUtil.makeUuidFilePath(uploadPath,uuidFileName);
InputStream is = new FileInputStream(uuidFilePath+"/"+uuidFileName);
//模式:/WEB-INF/upload/12/4/43213_cc.jpg
OutputStream os = response.getOutputStream();
buf = new byte[1024];
int len = 0;
while((len=is.read(buf))>0){
os.write(buf,0,len);
}
is.close();
os.close();
}
}
*1 文件上传下载和数据库结合
1)速度:较小文件的文件存入数据库中,取出速度较快,返之较慢。
较大文件的存入硬盘中,取出速度相对于数据库较快。
2)同步:
数据库表和硬盘必须一致,必须要事务的支持
在事务的情况下,表操作优先,硬盘其后
package cn.itcast.web.servlet.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;
}
}
package cn.itcast.web.servlet.dao;
import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import cn.itcast.web.servlet.domain.Up;
import cn.itcast.web.servlet.util.JdbcUtil;
public class UpDao {
//增加Up对象
public void addUp(Up up) throws SQLException{
QueryRunner runner = new QueryRunner(JdbcUtil.getDataSource());
String sql = "insert into up(username,realFileName,uuidFileName) values(?,?,?)";
runner.update(sql,new Object[]{up.getUsername(),up.getRealFileName(),up.getUuidFileName()});
}
}
<?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>
package cn.itcast.web.servlet.util;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.mchange.v2.c3p0.ComboPooledDataSource;
//JDBC工具类:关闭流和取得连接
public final class JdbcUtil {
private static ComboPooledDataSource dataSource;
static{
dataSource = new ComboPooledDataSource();
}
//取得数据源
public static ComboPooledDataSource getDataSource() {
return dataSource;
}
//取得连接
public static Connection getMySqlConnection() throws SQLException{
return dataSource.getConnection();
}
//关闭连接
public static void close(Connection conn) throws SQLException{
if(conn!=null){
conn.close();
}
}
public static void close(PreparedStatement pstmt) throws SQLException {
if(pstmt!=null){
pstmt.close();
}
}
public static void close(ResultSet rs) throws SQLException {
if(rs!=null){
rs.close();
}
}
}
package cn.itcast.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 cn.itcast.web.servlet.domain.Up;
import cn.itcast.web.servlet.domain.User;
import cn.itcast.web.servlet.exception.NoUpfileException;
import cn.itcast.web.servlet.exception.UpfileSizeException;
import cn.itcast.web.servlet.exception.UpfileTypeException;
import cn.itcast.web.servlet.service.UpService;
import cn.itcast.web.servlet.util.UploadUtil;
public class UploadServlet extends HttpServlet {
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<Up>();
//写入硬盘
UploadUtil.doSave(user,uploadPath,upList);
//写入数据库表
UpService upService = new UpService();
upService.addUps(upList);
request.setAttribute("message","上传文件成功");
request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response);
}catch(UpfileSizeException e){
e.printStackTrace();
request.setAttribute("message","<font color=‘green‘>上传文件大小限制在200K以内</font>");
request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response);
}catch(UpfileTypeException e){
e.printStackTrace();
request.setAttribute("message","<font color=‘red‘>只能上传JPG格式的文件</font>");
request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response);
}catch(NoUpfileException e){
e.printStackTrace();
request.setAttribute("message","<font color=‘blue‘>无上传文件</font>");
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);
}
}
}
文件上传表单 上传文件的细节 文件上传下载和数据库结合
标签: