JavaWeb实现文件上传和下载(多文件同时上传)

原创
2015/11/22 16:30
阅读数 626

    一、开发环境: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的,直接留下邮箱吧,看到就给你发过去。

展开阅读全文
打赏
1
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
1
分享
在线直播报名
返回顶部
顶部