一、开发环境:Ecplise , jdk1.7 , web3.0
二、常见web工程
创建步骤在这里就不详细说了,相信大家都一定是会的,下面给大家看下工程的结构图并说一下所得jar包
工程目录图:
在这个工程所需3个jar包,分别是:commons-fileupload-1.3.1.jar、commons-io-2.4.jar、jstl-1.2.jar,这个三个jar很好下,也可以直接去Apache官网下载,等下我会把源码附上,里面会包含所需要的jar包。
三、实现思路
1.实例Apache提供的文件上传下载组件实现功能。
2.为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名,我这里采用的是32位UUID。
3.为防止一个目录下面存放太多文件,需要将文件分来存放,因为一个文件夹下面存放着太多的文件,在获取文件的时候会耗费很长的时间,网上说有使用hash算法打散存储,我也试了这种储存方式,但是由于hash算法产生的目录是随机数据,我个人觉得并不适用在应用中,我在公司就处理过一次大批量文件的迁移,也就是前期赶项目的时候没有考虑太多,就把所有的文件都存放在一个目录下面了,导致我们的网站加载图片越来越慢了,解决方案是,图片文件按照大分类来存放,在分类文件下面,每年建一个文件夹,然后在年文件夹下面每月建一个文件夹,在月文件夹下面每天建一个文件夹,这样递归存放,就可以将文件分开存放,加载速度不会那么慢,执行完这个方案后,加载图片速度快多了。这里我就给大家讲一下这个思路,在demo的源码中就没有实现这个了,因为在存放文件,在下载的时候要利用数据库来获取相应的路径下载,demo的文件上传我全部放在某个文件夹的根目录下,方便后面的那个下载的功能。
4.要限制上传文件的最大值,这个很关键,要是上传一个很大的图片文件的话,用户在浏览网页的时候,加载时时间会很长,如果是APP的话,会耗费大量的流量,这是不可取的,一定的让运维对图片进行处理。
5、要限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法,这个相信大家都懂,就不累赘的讲了,下面直接开刷源码吧。
四、实现源码解析
由于没有采用任何框架,就用原生的servlet了。
1.创建文件上传的servlet FileUploadServlet.java
package com.liujiang.servlet;
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.annotation.WebServlet;
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;
/**
*
* @author Java文件上传
*
*/
@WebServlet("/FileUpload")
public class FileUploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* 上传成功
*/
private static final String SUCCESS = "文件上传结果:上传成功";
/**
* 上传失败
*/
private static final String ERROR = "文件上传结果:上传失败";
/**
* 表单中无数据
*/
private static final String FROM_EMPTY = "文件上传结果:表单中无数据";
/**
* 单个文件超出最大值
*/
private static final String FILE_SIZE_MAX = "文件上传结果:单个文件超出最大值";
/**
* 总文件超出最大值
*/
private static final String SIZE_MAX = "文件上传结果:总文件超出最大值";
/**
* 文件上传结果JSP页面
*/
private static final String MSG_JSP = "/message.jsp";
/**
* 构造函数
*/
public FileUploadServlet() {
super();
}
/**
* get函数
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
/**
* post函数
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置编码,解决表单数据乱码问题
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
//设置上传文件的存放目录,将上传的文件存放于WEB-INF目录下
String filePath = this.getServletContext().getRealPath("/WEB-INF/upload");
File file = new File(filePath);
//判断上传文件的保存目录是否存在,不存在则创建
if (!file.exists() && !file.isDirectory()) {
System.out.println(filePath+"目录不存在,需要创建");
//创建目录
file.mkdir();
}
System.out.println("文件上传到的目录:" + filePath);
//上传时生成的临时文件保存目录
String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp");
File tmpFile = new File(tempPath);
if (!tmpFile.exists()) {
//创建临时目录
tmpFile.mkdir();
}
//文件上传结果消息
String message = "";
try{
//使用Apache文件上传组件处理文件上传步骤:
//创建一个DiskFileItemFactory工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//设置工厂的缓冲区的大小,当上传的文件大小超过缓冲区的大小时,就会生成一个临时文件存放到指定的临时目录当中。
//设置缓冲区的大小为100KB,如果不指定,那么缓冲区的大小默认是10KB
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)){
//设置文件上传结果,并跳转至结果界面
request.setAttribute("message",FROM_EMPTY);
request.getRequestDispatcher(MSG_JSP).forward(request, response);
}
//设置上传单个文件的大小的最大值,目前是设置为1024*1024*2字节,也就是2MB
upload.setFileSizeMax(1024*1024*2);
//设置上传文件总量的最大值,最大值=同时上传的多个文件的大小的最大值的和,目前设置为10MB
upload.setSizeMax(1024*1024*10);
//使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每一个FileItem对应一个Form表单的输入项
List<FileItem> list = upload.parseRequest(request);
int i = 0;
for(FileItem item : list){
//如果fileitem中封装的是普通输入项的数据
if(item.isFormField()){
String name = item.getFieldName();
//解决普通输入项的数据的中文乱码问题
String value = item.getString("UTF-8");
//普通数据这里就不处理了,直接打印出值
System.out.println(name + "=" + value);
}
//如果fileitem中封装的是上传文件
else{
i++;
//得到上传的文件名称,
String filename = item.getName();
System.out.println("上传第" + i + "个的文件名:" + filename);
//文件为空,继续获取下一个文件
if(filename==null || filename.trim().equals("")){
continue;
}
//注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如: c:\a\b\c.png,而有些只是单纯的文件名,如:c.png
//处理获取到的上传文件的文件名的路径部分,只保留文件名部分
filename = filename.substring(filename.lastIndexOf(File.separator)+1);
//得到上传文件的扩展名
String fileExtName = filename.substring(filename.lastIndexOf(".")+1);
//如果需要限制上传的文件类型,那么可以通过文件的扩展名来判断上传的文件类型是否合法【按照实际情况,例如:图片,你就去校验一个扩展名是否是图片的扩展名】
System.out.println("上传的文件的扩展名是:"+fileExtName);
//获取item中的上传文件的输入流
InputStream in = item.getInputStream();
//得到文件保存的名称
String saveFilename = makeFileName(filename);
//创建一个文件输出流
FileOutputStream out = new FileOutputStream(filePath + File.separator + saveFilename);
//创建一个缓冲区
byte buffer[] = new byte[1024];
//判断输入流中的数据是否已经读完的标识
int len = 0;
//循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据
while((len=in.read(buffer))>0){
//使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(filePath + "\\" + filename)当中
out.write(buffer, 0, len);
}
//关闭输入流
in.close();
//关闭输出流
out.close();
//删除处理文件上传时生成的临时文件
item.delete();
//设置上传结果
message = SUCCESS;
System.out.println(SUCCESS);
}
}
}catch (FileUploadBase.FileSizeLimitExceededException e) {
e.printStackTrace();
request.setAttribute("message", FILE_SIZE_MAX);
System.out.println(FILE_SIZE_MAX);
request.getRequestDispatcher(MSG_JSP).forward(request, response);
return;
}catch (FileUploadBase.SizeLimitExceededException e) {
e.printStackTrace();
System.out.println(SIZE_MAX);
request.setAttribute("message", SIZE_MAX);
request.getRequestDispatcher(MSG_JSP).forward(request, response);
return;
}catch (Exception e) {
//上传中,出现异常,请分析异常信息
message= ERROR;
System.out.println(ERROR);
e.printStackTrace();
return;
}
//设置文件上传结果,并跳转至结果界面
request.setAttribute("message",message);
request.getRequestDispatcher(MSG_JSP).forward(request, response);
}
/**
*
* @param 获取唯一文件名,这里采用UUID生成
* @return
*/
private String makeFileName(String filename){
//为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名
return UUID.randomUUID().toString() + "_" + filename;
}
}
对应的web.xml配置
<!-- 文件上传servlet 开始 -->
<servlet>
<servlet-name>FileUploadServlet</servlet-name>
<servlet-class>com.liujiang.servlet.FileUploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FileUploadServlet</servlet-name>
<url-pattern>/fileUpload</url-pattern>
</servlet-mapping>
<!-- 文件上传servlet 结束-->
jsp页面:
文件上传的jsp
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JavaWeb文件上传Demo</title>
</head>
<body>
<center>
<h1>Java文件上传Demo</h1>
<div>
<!-- 文件上传的表单比普通的多个属性 enctype="multipart/form-data"-->
<form id="form" action="fileUpload" class="form-horizontal" enctype="multipart/form-data" method="post">
<div class="form-group">
<label class="col-sm-2 control-label no-padding-right" for="seqNum"><font color="red">*</font>上传文件文件 </label>
<div class="col-sm-8">
<div class="clearfix">
上传文件1:<input type="file" name="file1"><br/>
上传文件2:<input type="file" name="file2"><br/>
<input type="submit" value="提交">
</div>
</div>
</div>
</form>
</div>
<br/>
<br/>
<br/>
</center>
</body>
</html>
操作结果消息jsp
message.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>文件上传结果</title>
</head>
<body>
${message}
</body>
</html>
文件上传的代码就结束了。
文件下载
首先得吧所有文件的显示在页面上
创建FileListVeiw servlet
package com.liujiang.servlet;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 文件列表servlet
* @author Ocean
*
*/
@WebServlet("/FileListVeiw")
public class FileListVeiwServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* 构造函数
*/
public FileListVeiwServlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* get函数
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
/**
* post函数
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取文件的所在目录
String uploadFilePath = this.getServletContext().getRealPath("/WEB-INF/upload");
System.out.println("文件的所在目录" + uploadFilePath);
//存储要下载的文件名
Map<String,String> fileNameMap = new HashMap<String,String>();
//递归遍历filepath目录下的所有文件和目录,将文件的文件名存储到map集合中
fileList(new File(uploadFilePath),fileNameMap);//File既可以代表一个文件也可以代表一个目录
//将Map集合发送到listfile.jsp页面进行显示
request.setAttribute("filelist", fileNameMap);
request.getRequestDispatcher("/fileList.jsp").forward(request, response);
}
/**
* 递归遍历指定目录下的所有文件
* @param file 递归遍历指定目录下的所有文件
* @param map 存储文件名的Map集合
*/
public void fileList(File file,Map<String,String> map){
//如果file代表的不是一个文件,而是一个目录
if(!file.isFile()){
//列出该目录下的所有文件和目录
File files[] = file.listFiles();
//遍历files[]数组
for(File f : files){
//递归
fileList(f,map);
}
}else{
String realName = file.getName().substring(file.getName().indexOf("_")+1);
//file.getName()得到的是文件的原始名称,这个名称是唯一的,因此可以作为key,realName是处理过后的名称,有可能会重复
map.put(file.getName(), realName);
}
}
}
创建显示的jsp页面,fileList.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>所有图片列表</title>
</head>
<body>
<center>
<h1>文件列表</h1>
<!-- 遍历Map集合 -->
<c:forEach items="${filelist}" var="fileName">
<c:url value="fileDownLoad" var="downurl">
<c:param name="filename" value="${fileName.key}"></c:param>
</c:url>${fileName.value}<a href="${downurl}">下载</a>
<br/>
</c:forEach>
</center>
</body>
</html>
效果:
文件下载:
创建文件下载 servlet FileDownLoadServletjava
package com.liujiang.servlet;
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.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 文件下载servlet
* @author Ocean
*
*/
@WebServlet("/FileDownLoadServlet")
public class FileDownLoadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* 构造函数
*/
public FileDownLoadServlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* get函数
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
/**
* post函数
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//得到要下载的文件名
String fileName = request.getParameter("filename");
fileName = new String(fileName.getBytes("iso8859-1"),"UTF-8");
//上传的文件都是保存在/WEB-INF/upload目录下的子目录当中
String rootPath=this.getServletContext().getRealPath("/WEB-INF/upload");
//文件的物理路径,在实际的应用中会把根目录之下的文件路径存放在数据库中
String path = String.format("%s%s%s", rootPath,File.separator,fileName);
//得到要下载的文件
File file = new File(path);
//如果文件不存在
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);
//创建输出流
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();
}
}
效果图:
到此,整个demo就全部分享完了,如果有什么错误,欢迎大牛斧正,谢谢!!!
突然发现不能上传附件,如有需要demo的,直接留下邮箱吧,看到就给你发过去。