<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> </head> <body> <!-- 1. 表单提交方式post 2. 表单的enctype="multipart/form-data" 1. 表单要上传文件输入项type="file" --> <form method="post" enctype="multipart/form-data" action="/upload/upload"> 上传人: <input type="text" name="name"><br/> 请上传图片:<input type="file" name="file"><br> <input type="submit" value="上传"> </form> </body></html>
1.1.2编写servlet处理提供的数据fileupload组件工作流程
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.FileUploadException;import org.apache.commons.fileupload.disk.DiskFileItemFactory;import org.apache.commons.fileupload.servlet.ServletFileUpload;
/**
完成文件上传 */public class UploadServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { / 由于form的enctype属性原因,以下代码获取不了上传数据: String * name=request.getParameter("name"); String * file=request.getParameter("file"); ServletInputStream in = * request.getInputStream(); */
// apache提供了开源的commons-fileupload组件,只需要在工程中导入使用// 1.导入commons-fileupload-1.2.1.jar,commons-io-1.4.jar到lib// 2.拿到工厂DiskFileItemFactory factory = new DiskFileItemFactory();// 3.拿到解析器对象ServletFileUpload uploader = new ServletFileUpload(factory);// 4.关联到requestList<FileItem> list;try { list = uploader.parseRequest(request); for (FileItem fileItem : list) { if (fileItem.isFormField()) { // 普通的输入项 // 获得普通输入项字段的名称 String fieldName = fileItem.getFieldName(); // 获得普通输入项字段的名称对应的值 String fieldValue = fileItem.getString(); } else { // 文件上传输入项 // 获得上传文件名字 String name = fileItem.getName(); // 获得上传文件输入流 InputStream in = fileItem.getInputStream(); OutputStream out = new FileOutputStream("D://" + name); int len = 0; byte[] buf = new byte[1024]; while ((len = in.read(buf)) != -1) { out.write(buf, 0, len); } in.close(); out.close(); } }} catch (Exception e) { // TODO Auto-generated catch block e.PRintStackTrace();}
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response);}}
##### 1.2 文件上传若干需要注意的细节 1. 文件上传时普通的输入项中文的乱码问题
java//这里使用UTF-8进行解码String fieldValue=fileItem.getString("UTF-8");2. 上传时中文文件名称乱码
java//设置解析事,中文文件名解析的码表uploader.setHeaderEncoding("UTF-8");3. 上传的时候,表单必须设置三个值.判断文件上传的时候,提交的数据是否是文件上传的表单
java//判断是否文件上传的表单if(!ServletFileUpload.isMultipartContent(request)){ //说明不是文件上传的表单,下面的代码不再执行 request.setAttribute("message", "对不起,您的表单不符合规范,请检查表单属性设置..."); request.getRequestDispatcher("/upload.jsp").forward(request, response); return;}4. 限定上传文件的大小.
java//限定上传文件的大小uploader.setFileSizeMax(102410242);//以kb为单位,限制单个文件大小uploader.setSizeMax(1024102420);//限制总的文件大小20M当超过大小限制时,就会抛出异常.就可以捕获异常
javacatch (FileUploadBase.FileSizeLimitExceededException e) { // 单个文件大小超过限制 request.setAttribute("message", "对不起,上传单个文件大小超过限制..."); request.getRequestDispatcher("/upload.jsp").forward(request, response); } catch (FileUploadBase.SizeLimitExceededException e) { // 总的文件大小超过限制 request.setAttribute("message", "对不起,总的文件大小超过限制..."); request.getRequestDispatcher("/upload.jsp").forward(request, response); }5. 限制上传的文件类型
java//将允许的文件类型准备好String[] extensions={".jpg",".bmp",".png"};如果不合法
javaboolean flag = false;for (String ex : extensions) { if (name.endsWith(ex)) { flag = true; break; }}if (!flag) { // 文件类型不合法,需要给用户友好信息 request.setAttribute("message", "对不起,文件类型不合法..."); request.getRequestDispatcher("/upload.jsp").forward( request, response); return;}6. 针对不同浏览器解决文件上传名称的问题
javaString name = fileItem.getName();// 有些浏览器获得的name是E:/1.bmp// 这个索引是最后一个/所在索引位置int lastIndex = name.lastIndexOf("/");if (lastIndex != -1) { name = name.substring(lastIndex + 1);}7. 解决文件上传时,同名文件被覆盖问题.为了解决这个问题,生成文件名字要唯一,可以使用java提供的UUID的类.生成一个随机128值.
java//生成不同名文件名,避免覆盖问题 name=UUID.randomUUID().toString().replace("-", "")+"_"+name;8. 上传的内容是恶意内容,网站被攻击 将上传文件放在一个特定文件夹下,不让外界直接访问.上传文件放在WEB-INF目录下保护起来.
java//获得WEB-INF目录下upload文件夹的路径 String filePath=getServletContext().getRealPath("/WEB-INF/upload"); InputStream in = fileItem.getInputStream(); FileOutputStream out = new FileOutputStream(new File(filePath,UUIDName));
9. 避免上传文件夹文件过多,导致打开文件夹出现性能上问题,比如文件过多,打开较慢需要设计一套算法,保障上传文件分散放在多级目录
1.每隔一段时间创建一个文件夹,然后放上传的文件2.哈希算法,根据hash值二进制每隔4位的值创建一层目录.javaprivate String generatedRandomDir(String filePath, String uUIDName) { int hashCode = uUIDName.hashCode(); int dir1 = hashCode & 0xf;// 一级目录 int dir2 = (hashCode >> 4) & 0xf;// 二级目录 String path = filePath + File.separator + dir1 + File.separator + dir2; File file = new File(path); if (!file.exists()) { file.mkdirs(); } return path; }
10. 上传时设置监听器监听上传进度
javaProgressListener progressListener = new ProgressListener() { private long megaBytes = -1;
public void update(long pBytesRead, long pContentLength, int pItems) { long mBytes = pBytesRead / 1000000; if (megaBytes == mBytes) { return; } megaBytes = mBytes; System.out.println("We are currently reading item " + pItems); if (pContentLength == -1) { System.out.println("So far, " + pBytesRead + " bytes have been read."); } else { System.out.println("So far, " + pBytesRead + " of " + pContentLength + " bytes have been read."); } } }; // 设置监听器一定在解析之前设置 uploader.setProgressListener(progressListener);
11. 为了加快上传速度,可以设置临时缓冲区大小以及上传时临时文件存放位置.上传完成后,临时文件给删掉.
java//设置临时缓存区大小factory.setSizeThreshold(102412042);//2M//设置上传时,临时文件的位置factory.setRepository(new File(getServletContext().getRealPath("/tmp")));java//删除临时文件,该方法放在流关闭之后fileItem.delete();
实现代码
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 Upload2Servlet extends HttpServlet { /** * 文件上传,需要注意的问题 */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 将允许的文件类型准备好 String[] extensions = { ".jpg", ".bmp", ".png" };
// 判断是否文件上传的表单 if (!ServletFileUpload.isMultipartContent(request)) { // 说明不是文件上传的表单,下面的代码不再执行 request.setAttribute("message", "对不起,您的表单不符合规范,请检查表单属性设置..."); request.getRequestDispatcher("/upload.jsp").forward(request, response); return; } try { DiskFileItemFactory factory = new DiskFileItemFactory(); //设置临时缓存区大小 factory.setSizeThreshold(1024*1204*2);//2M //设置上传时,临时文件的位置 factory.setRepository(new File(getServletContext().getRealPath("/tmp"))); ServletFileUpload uploader = new ServletFileUpload(factory); // 限定上传文件的大小 uploader.setFileSizeMax(1024 * 1024 * 2);// 以kb为单位,限制单个文件大小 uploader.setSizeMax(1024 * 1024 * 20);// 限制总的文件大小20M // 设置解析事,中文文件名解析的码表 uploader.setHeaderEncoding("UTF-8"); // 设置监听器,监听上传进度 // 这里的update方法不定时被解析器调用到, // pBytesRead:上传文件已经解析多少 // pContentLength:上传单个文件总的大小 // pItems:当前上传的是第几个Item ProgressListener progressListener = new ProgressListener() { private long megaBytes = -1; public void update(long pBytesRead, long pContentLength, int pItems) { long mBytes = pBytesRead / 1000000; if (megaBytes == mBytes) { return; } megaBytes = mBytes; System.out.println("We are currently reading item " + pItems); if (pContentLength == -1) { System.out.println("So far, " + pBytesRead + " bytes have been read."); } else { System.out.println("So far, " + pBytesRead + " of " + pContentLength + " bytes have been read."); } } }; // 设置监听器一定在解析之前设置 uploader.setProgressListener(progressListener); List<FileItem> list = uploader.parseRequest(request); for (FileItem fileItem : list) { if (fileItem.isFormField()) { String fieldName = fileItem.getFieldName(); // 这里使用UTF-8进行解码 String fieldValue = fileItem.getString("UTF-8"); } else { String name = fileItem.getName(); // 有些浏览器获得的name是E:/1.bmp.针对不同浏览器解决上传时文件名称问题 // 这个索引是最后一个/所在索引位置 int lastIndex = name.lastIndexOf("//"); if (lastIndex != -1) { name = name.substring(lastIndex + 1); } boolean flag = false; for (String ex : extensions) { if (name.endsWith(ex)) { flag = true; break; } } if (!flag) { // 文件类型不合法,需要给用户友好信息 request.setAttribute("message", "对不起,文件类型不合法..."); request.getRequestDispatcher("/upload.jsp").forward( request, response); return; } // 获得WEB-INF目录下upload文件夹的路径 String filePath = getServletContext().getRealPath( "/WEB-INF/upload"); // 生成不同名文件名,避免覆盖问题 String UUIDName = UUID.randomUUID().toString() .replace("-", "") + "_" + name; // 生成随机文件夹,保存上传文件 filePath = generatedRandomDir(filePath, UUIDName); InputStream in = fileItem.getInputStream(); FileOutputStream out = new FileOutputStream(new File( filePath, UUIDName)); int len = 0; byte[] buf = new byte[1024]; while ((len = in.read(buf)) != -1) { out.write(buf, 0, len); } in.close(); out.close(); //删除临时文件,该方法放在流关闭之后 fileItem.delete(); } } } catch (FileUploadBase.FileSizeLimitExceededException e) { // 单个文件大小超过限制 request.setAttribute("message", "对不起,上传单个文件大小超过限制..."); request.getRequestDispatcher("/upload.jsp").forward(request, response); } catch (FileUploadBase.SizeLimitExceededException e) { // 总的文件大小超过限制 request.setAttribute("message", "对不起,总的文件大小超过限制..."); request.getRequestDispatcher("/upload.jsp").forward(request, response); } catch (Exception e) { e.printStackTrace(); }}private String generatedRandomDir(String filePath, String uUIDName) { int hashCode = uUIDName.hashCode(); int dir1 = hashCode & 0xf;// 一级目录 int dir2 = (hashCode >> 4) & 0xf;// 二级目录 String path = filePath + File.separator + dir1 + File.separator + dir2; File file = new File(path); if (!file.exists()) { file.mkdirs(); } return path;}public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response);}public static void main(String[] args) { System.out.println(UUID.randomUUID().toString().replace("-", ""));}
}
#### 2. 文件下载1. 告诉浏览器以下载方式打开要访问的数据
//设置content-diposition:attachement:filename=1.txt2. 将下载的文件当作一个流与response.getOutputStream关联起来.
javaInputStream in=new FileInputStream("d:/aa/bb/1.txt");OutputStream out=response.getOutStream();----IO流拷贝```
要求:
步骤:
sql create database upload_download; use upload_download; create table upfiles ( id varchar(100) primary key, uuidname varchar(80), filename varchar(80), savepath varchar(150), uploadtime timestamp,//数据库自动将当前时间插入 description varchar(255), username varchar(10) );
导入jar包
准备封装数据的javaBean java package com.domain; import java.sql.Timestamp; /** create table upfiles ( id varchar(100) primary key, uuidname varchar(80), filename varchar(80), savepath varchar(150), uploadtime timestamp, description varchar(255), username varchar(10) ); */ public class UpFile { private String id; private String uuidname;//上传文件的名称,文件的uuid名 private String filename;//上传文件的真实名称 private String savepath;//上传文件的路径 private Timestamp uploadtime;//上传时间 private String description;//文件描述 private String username;//上传人 public String getId() { return id; } public void setId(String id) { this.id = id; } public String getUuidname() { return uuidname; } public void setUuidname(String uuidname) { this.uuidname = uuidname; } public String getFilename() { return filename; } public void setFilename(String filename) { this.filename = filename; } public String getSavepath() { return savepath; } public void setSavepath(String savepath) { this.savepath = savepath; } public Timestamp getUploadtime() { return uploadtime; } public void setUploadtime(Timestamp uploadtime) { this.uploadtime = uploadtime; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } }
package com.web; import java.io.IOException; import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException;import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;import org.apache.commons.fileupload.servlet.ServletFileUpload; import service.UpfileService; import com.domain.UpFile;import com.utils.WebUtils; public class UploadServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 判断是否是文件上传的request if (!ServletFileUpload.isMultipartContent(request)) { // 说明不是文件上传的表单,下面的代码不再执行 request.setAttribute("message", "对不起,您的表单不符合规范,请检查表单属性设置..."); request.getRequestDispatcher("/upload.jsp").forward(request, response); return; } // 封装一个方法,完成文件上传 UpFile upFile; try { upFile = WebUtils.doFileUpload(request); } catch (FileSizeLimitExceededException e) { request.setAttribute("message", "对不起,上传单个文件大小超过限制..."); request.getRequestDispatcher("/upload.jsp").forward(request, response); return; } catch (SizeLimitExceededException e) { request.setAttribute("message", "对不起,总的文件大小超过限制.....20M."); request.getRequestDispatcher("/upload.jsp").forward(request, response); return; } // 判断upFile是否为空,为空不执行后面语句 if (upFile == null) { return; } System.out.println(upFile); UpfileService ups = new UpfileService(); ups.upload(upFile); // 上传成功,给用户友好提示 response.setContentType("text/html;charset=utf-8"); response.getWriter().print( "上传成功<a href='" + request.getContextPath() + "/upload.jsp'>再次上传</a>"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
package com.utils; 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.HttpServletRequest;import javax.servlet.http.HttpServletResponse; import org.apache.commons.beanutils.BeanUtils;import org.apache.commons.fileupload.FileItem;import org.apache.commons.fileupload.FileUploadBase;import org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException;import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;import org.apache.commons.fileupload.ProgressListener;import org.apache.commons.fileupload.disk.DiskFileItemFactory;import org.apache.commons.fileupload.servlet.ServletFileUpload; import com.domain.UpFile; public class WebUtils { public static UpFile doFileUpload(HttpServletRequest request) throws FileSizeLimitExceededException, SizeLimitExceededException { UpFile upFile = new UpFile(); try { // 拿到工厂 DiskFileItemFactory factory = new DiskFileItemFactory(); // 设置临时缓存区大小 factory.setSizeThreshold(1024 * 1204 * 2);// 2M // 设置上传时,临时文件的位置 factory.setRepository(new File(request.getsession() .getServletContext().getRealPath("/tmp"))); ServletFileUpload uploader = new ServletFileUpload(factory); // 限定上传文件的大小 uploader.setFileSizeMax(1024 * 1024 * 2);// 以kb为单位,限制单个文件大小 uploader.setSizeMax(1024 * 1024 * 20);// 限制总的文件大小20M // 设置解析事,中文文件名解析的码表 uploader.setHeaderEncoding("UTF-8"); // 设置监听器,监听上传进度 // 这里的update方法不定时被解析器调用到 // pBytesRead:上传文件已经解析多少 // pContentLength:上传单个文件总的大小 // pItems:当前上传的是第几个Item ProgressListener progressListener = new ProgressListener() { private long megaBytes = -1; public void update(long pBytesRead, long pContentLength, int pItems) { long mBytes = pBytesRead / 1000000; if (megaBytes == mBytes) { return; } megaBytes = mBytes; System.out.println("We are currently reading item " + pItems); if (pContentLength == -1) { System.out.println("So far, " + pBytesRead + " bytes have been read."); } else { System.out.println("So far, " + pBytesRead + " of " + pContentLength + " bytes have been read."); } } }; // 设置监听器一定在解析之前设置 uploader.setProgressListener(progressListener); List<FileItem> list = uploader.parseRequest(request); for (FileItem fileItem : list) { if (fileItem.isFormField()) { String fieldName = fileItem.getFieldName(); // 这里使用UTF-8进行解码 String fieldValue = fileItem.getString("UTF-8"); // 使用beanUtils封装java类 // beanUtils封装数据,要求目标javaBean字段名称与表单提交过来的数据的名称一样,否则封装不上 BeanUtils.setProperty(upFile, fieldName, fieldValue); } else { String filename = fileItem.getName(); // 有些浏览器获得的name是E:/1.bmp.针对不同浏览器解决上传时文件名称问题 // 这个索引是最后一个/所在索引位置 int lastIndex = filename.lastIndexOf("//"); if (lastIndex != -1) { filename = filename.substring(lastIndex + 1); } // 获得WEB-INF目录下upload文件夹的路径 String savepath = request.getSession().getServletContext() .getRealPath("/WEB-INF/upload"); // 生成不同名文件名,避免覆盖问题 String uuidname = UUID.randomUUID().toString() .replace("-", "") + "_" + filename; // 生成随机文件夹,保存上传文件,避免性能问题 savepath = generatedRandomDir(savepath, uuidname); InputStream in = fileItem.getInputStream(); FileOutputStream out = new FileOutputStream(new File( savepath, uuidname)); int len = 0; byte[] buf = new byte[1024]; while ((len = in.read(buf)) != -1) { out.write(buf, 0, len); } in.close(); out.close(); // 删除临时文件 fileItem.delete(); upFile.setFilename(filename); upFile.setId(UUID.randomUUID().toString()); upFile.setUuidname(uuidname); upFile.setSavepath(savepath); } } return upFile; } catch (FileUploadBase.FileSizeLimitExceededException e) { throw e; } catch (FileUploadBase.SizeLimitExceededException e) { throw e; } catch (Exception e) { e.printStackTrace(); return null; } } private static String generatedRandomDir(String savepath, String uuidname) { int hashCode = uuidname.hashCode(); int dir1 = hashCode & 0xf;// 一级目录 int dir2 = (hashCode >> 4) & 0xf;// 二级目录 String path = savepath + File.separator + dir1 + File.separator + dir2; File file = new File(path); if (!file.exists()) { file.mkdirs(); } return path; }}
新闻热点
疑难解答