文件上传与下载

文件上传下载概述

1、什么是文件上传下载

所谓文件上传下载就是将本地文件上传到服务器端,从服务器端下载文件到本地的过程。

例如目前网站需要上传头像、上传下载图片或网盘等功能都是利用文件上传下载功能实现的。

文件上传下载实际上是两步操作,

第一是文件上传,就是将本地文件上传到服务器端,实现文件多用户之间的共享,

第二是文件下载,就是将服务器端的文件下载到本地磁盘。

2.文件上传下载实现原理

上传:


文件上传实现流程如下:

Ø  客户端浏览器通过文件浏览框,选择需要上传的文件内容(其中包括文件路径及文件内容)。

Ø  客户端浏览器通过点击上传按钮,将本地文件上传到服务器端。

Ø  服务器端通过程序接收本地文件内容,并将其保存在服务器端磁盘中。

下载:


文件下载实现流程如下:

Ø  客户端浏览器通过点击下载按钮,将服务器端保存的文件下载到本地磁盘。

Ø  服务器端通过程序将服务器端文件响应给客户端。

3、实现上传

文件上传到客户端页面:

在Web应用程序中实现文件上传功能,只需要在客户端页面中添加需要上传输入项,在服务器端Servlet中读取上传文件的数据,并保存在服务器端硬盘中即可。

需要注意的是:

Ø 文件上传域(<input type='file'>)必须具有name属性.: 

<inputtype=”file”>标签必须指定name属性值,否则需要上传的文件数据是不会上传至服务器端。

Ø 请求方式一定是POST.: 

完成文件上传功能的表单的请求类型必须是POST方式。

Ø  表单的enctype属性值设置为"multipart/form-data".

完成文件上传功能的表单的enctype属性值设置为“multipart/form-data”,该值的作用是将需要上传的文件数据添加到Http请求体中,并使用MIME协议对上传的文件进行描述。

 <body>
    <form action="" method="post" enctype="multipart/form-data">
    	文件描述:<input type="text" name="filetext"><br>
    	<input type="file" name="upload"><br>
    	<input type="submit" value="上传">
    </form>
  </body>


commons-fileupload工具

完成客户端的文件上传功能之后,主要是在服务器端完成接收上传文件的数据内容。为了方便实现文件上传逻辑,可以使用第三方提供的文件上传包,具体如下:

Ø  jsp-smartupload.jar:使用JSP模型一时使用的,目前基本不再使用。

Ø  commons-fileupload.jar:由Apache基金会提供的,用来实现Java环境下的文件上传功能。

Ø  Servlet3.0规范中提供对文件上传的支持。

commons-fileupload组件的官网地址:http://commons.apache.org/proper/commons-fileupload/。需要注意的是:在使用commons-fileupload组件时,需要依赖于commons-io包。commons-fileupload组件工作流程如下:


实现文件上传的服务器端:
UploadServlet.:
package 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 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;
import org.apache.commons.io.IOUtils;
//完成文件上传功能
public class UploadServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		//1.创建文件上传的工厂类实例对象
		DiskFileItemFactory factory = new DiskFileItemFactory();
		//2.创建文件上传的servletFileUpload实例对象
		ServletFileUpload upload = new ServletFileUpload(factory);
		
		try {
			//3.利用ServletFileUpload实例对象解析request对象中有关文件上传的内容(文件上传的内容都封装在了request对象中了)
			List<FileItem> items = upload.parseRequest(request);
			//4.遍历文件上传到的list集合,得到有关文件上传到的所有内容(普通字段和上传内容)
			for (FileItem fileItem : items) {
				/*
				 * 如何可以知道当前的FileItem是普通项还是文件项
				 * *通过FileItem.isFormField()方法判断当前是普通项还是文件项
				 * *结果是true的话,表示当前是普通项
				 * *结果是false的话,表示当前是文件项
				 */
				if(fileItem.isFormField()){
					//普通项
					/*getFieldName():获取普通项的name属性值
					 *getString(): 获取普通项的文本内容
					 */
					String fieldName = fileItem.getFieldName();
					String fieldValue = fileItem.getString();
					System.out.println(fieldName+":"+fieldValue); //filetext : readme
				}else{
					//文件项
					/*getName():获取文件项的上传文件名称
					 *getInputStream(): 获取文件项的上传文件输入流
					 */
					String fileName = fileItem.getName();
					System.out.println(fileName);//readme.txt
					InputStream in = fileItem.getInputStream();
					
					String realPath = getServletContext().getRealPath("/uploads");
					OutputStream out = new FileOutputStream(new File(realPath,fileName));
					
					IOUtils.copy(in, out);
					
					in.close();
					out.close();
					
				}
			}
		} catch (FileUploadException e) {
			
			e.printStackTrace();
		}

	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}

}
upload.jsp:
<body>
    <form action="${pageContext.request.contextPath }/servlet/UploadServlet" method="post" enctype="multipart/form-data">
    	文件描述:<input type="text" name="filetext"><br>
    	<input type="file" name="upload"><br>
    	<input type="submit" value="上传">
    </form>
  </body>
导入工具包:commons-fileupload.jar和commons-io.jar包.
实现的步骤:
       * 实例化DiskFileItemFactory工厂类.
       * 实例化ServletFileUpload类.
         ServletFileUpload upload = new ServletFileUpload(factory);
       * 利用upload的parseRequest(request)方法从Request对象中获取文件上传的内容(List集合).
       * 遍历获取到的List集合.
         * 如果是普通项 - 获取普通项的文本内容.
  * isFormField():判断当前是否是普通项,true表示是.
  * getFieldName():获取普通项的name属性值.
  * getString():获取普通项的文本内容.
* 如果是文件项
  * getName():获取上传文件的名称.
  * getInputStream():获取上传文件的输入流.
  * 通过保存的路径,创建文件的输出流.
  * 利用IOUtils.copy(inputStream,OutputStream)方法将上传文件进行保存.(在服务器中查看)

动态多文件上传表单

想实现多文件上传功能的话,服务器端的逻辑是一样的,也就是说,只需要在客户端页面实现多文件上传控件即可。
<script type="text/javascript">
  	function addFileItem() {
		document.getElementById("showFile").innerHTML+="<div><input type='file' name='upload'>
		<input type='button' id='delFile' οnclick='delFileItem(this);' value='删除'><br></div>";
		
	}
  	function delFileItem(button) {
		var parentDiv = button.parentNode;
		parentDiv.parentNode.removeChild(parentDiv);
	}
  
  </script>
  <body>
    <form action="${pageContext.request.contextPath }/servlet/UploadServlet" method="post" enctype="multipart/form-data">
    	<input type="button" id="addFile" οnclick="addFileItem();" value="文件上传"><br>
    	<div id="showFile"></div>
    	<input type="submit" value="上传">
    </form>
  </body>


上传文件至WEB-INF目录

到目前步骤,已经可以成功从客户端浏览器向服务器端上传文件。但是上传的路径存在一些问题,上述上传路径是自定义的文件夹,而上传至这种自定义的文件夹后,通过浏览器可以正常访问,这是非常危险的。

例如一个用户上传一个JSP页面,然后通过浏览器访问该JSP页面,而该JSP页面中可以包含一些恶意代码。这时如果允许用户运行该JSP页面的话,可能会对服务器端造成很大影响。

所以,通常情况下,会将上传目录创建在Web工程的WEB-INF目录下。因为该目录下的内容,是无法通过浏览器访问到的。

WEN-INF目录外与WEB-INF目录中的区别:
         * WEN-INF目录外:在浏览器中可以访问的(安全低).
* WEB-INF目录中:在浏览器中不能访问的.
获取文件上传路径的代码,应该修改为如下内容:

String uploadPath =getServletContext().getRealPath("/WEB-INF/upload");


上传文件名称的处理

对于上传文件的名称,可能是文件的完整路径,例如:C:\upload\aaa.jpg。
可能使用的浏览器版本过旧,获取到的文件上传的名称,而是选择上传文件的完整路径.
 C:\Users\JYL\Desktop\day0106\笔记\readme.txt
在服务器端只需要保存其上传文件的名称即可,所以需要对上传文件的名称进行进一步地处理
String fileName = fileItem.getName();
//解决浏览器过旧的问题:上传文件名的处理 :C:\Users\JYL\Desktop\day0106\笔记\readme.txt
int index = fileName.lastIndexOf("\\");
if(index>=0){
fileName.substring(index+1);
}


上传文件同名问题的处理

如果同一个用户上传多个同名的文件,默认情况下会出现被覆盖的情况,即前一次上传的文件会被后一次上传的文件覆盖。而这种情况是不希望看到的,解决这个问题的方法就是可以为每一个上传的文件名称增加UUID,因为UUID类的randomUUID()可以生成一个唯一标识符。
问题:默认情况下,上传多次同名的文件时,新的文件会覆盖旧的文件.
解决:将每个上传的文件名,提供一个唯一的标识(拼在文件名中).
fileName = UUID.randomUUID().toString()+fileName;
注意:如果开发真实案例时,需要保存真实文件名称和上传后处理的文件名称.
//为了防止文件同名,给文件名增加一个唯一的id
fileName = UUID.randomUUID().toString()+fileName;

上传文件中文乱码问题

如果现在上传文件的名称为中文的话,会引起中文乱码问题。commons-fileupload组件为解决中文乱码问题提供了两种解决方案,如下:

Ø  利用Request对象的setCharacterEncoding(“UTF-8”)方法,该方法尽量编写在Servlet的doGet()或doPost()方法的顶端。

Ø  利用ServletFileUpload类提供的setHeaderEncdoing(“UTF-8”)方法来解决。

一般情况下,不关心上传文件的内容,因为上传文件会保存在服务器端的磁盘中。但是,如果需要在控制台打印上传文件的内容,而刚好该上传文件的内容中包含中文的话,可以使用FileItem的getString(“UTF-8”)来处理编码。



一个目录不能存放过多文件

如果上传文件过多时,会导致上传目录中的文件过多,内容过大。这时可以考虑将不同文件存储在不同的目录中,而生成不同目录的规则参考如下:

Ø  按照上传时间进行目录分离,例如2014-12-12为一个目录。

Ø  按照上传用户进行目录分离,为每一个用户创建一个上传目录。

Ø  按照固定数量进行目录分离,设定当一个上传目录包含文件超过指定数量,创建新的上传目录。

Ø  按照唯一文件名的hashcode进行目录分离。


这里以hashcode进行目录分离方式为例演示,具体思路如下:

Ø  使用UUID类的randomUUID()方法生成唯一标识符。

name = UUID.randomUUID().toString() + "_" + name;

其值为2dab369c-1e4f-4e58-8b61-13c7aef855b0。

Ø  通过hashCode()方法获取其唯一标识符的hashcode值。

int hashcode = uuidFileName.hashCode();

其值为:166846237,转换成二进制后的值为:1001111100011101111100011101。

Ø  按照每4位值“与”二进制1111(F)后,生成一级目录。

// 获得一级目录

int d1 = hashcode & 0xf;

Ø  以此类推,每4位值“与”二进制1111后,生成一个级别的目录。

// 获得二级目录

int d2 = (hashcode >>> 4) & 0xf;

根据上述步骤,可以编写一个按照hashcode方式生成目录的工具类,具体代码如下:

public class UploadUtils {
		// 根据唯一文件名生成 hashcode目录分离算法
		public static String generateRandomDir(String uuidFileName) {
			// 获得唯一文件名的hashcode
			int hashcode = uuidFileName.hashCode();
			// 获得一级目录
			int d1 = hashcode & 0xf;
			// 获得二级目录
			int d2 = (hashcode >>> 4) & 0xf;
			return "/" + d2 + "/" + d1; 
		}
	}

上述程序代码可以改写如下:

String randomDir = UploadUtils.generateRandomDir(name);

String uploadPath = getServletContext().getRealPath("/WEB-INF/upload" + randomDir);
// 生成随机目录
new File(uploadPath).mkdir();

上传单个文件的大小限制

上传文件时,用户可能上传非常大的文件,可能导致上传时占用过多资源。所以,对于用户上传的单个文件大小,应做出相应限制。利用ServletFileUpload的setFileSizeMax(long)方法进行设置,其中参数表示设置的大小,单位为字节数,例如servletFileUpload.setFileSizeMax(1024*10)表示上限为10KB。

一旦上传的单个文件大小超过限制大小时,会抛出FileUploadBase.FileSizeLimitExceededException异常,可以捕获该异常后向页面输出相应的错误信息。
单个文件大小的限制:3M
         * 利用ServletFileUpload的setFileSizeMax(字节数).
* 问题:如果上传的单个文件大小,大于限制的大小时:抛异常.
  FileSizeLimitExceededException
public class UploadServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doPost(request, response);
	}
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		DiskFileItemFactory factory = new DiskFileItemFactory();
		ServletFileUpload upload = new ServletFileUpload(factory);
		// 设置单个上传文件的大小
		upload.setFileSizeMax(1024 * 10);
		try {
			List<FileItem> items = upload.parseRequest(request);
			for (FileItem fileItem : items) {
				if (fileItem.isFormField()) {
					……
				}else{
					String name = fileItem.getName();
					……
					in.close();
				}
			}
		} catch (FileUploadException e) {
			if (e instanceof FileUploadBase.FileSizeLimitExceededException) {
				// 在request中保存错误信息
				request.setAttribute("msg", "上传失败!上传的文件超出了10KB!");
				// 转发到index.jsp页面中!在index.jsp页面中需要使用${msg}来显示错误信息
	request.getRequestDispatcher("/index.jsp").forward(request, response);
			}
			e.printStackTrace();
		}
	}
}

上传文件的总大小的限制

在实现多文件上传时,还需要设置上传文件的总大小。利用ServletFileUpload的setSizeMax(long)方法进行设置,其中参数表示设置的大小,单位为字节数,例如servletFileUpload.setSizeMax(1024*10)表示上限为10KB。

一旦上传的文件大小超过限制大小时,会抛出FileUploadBase.SizeLimitExceededException异常,可以捕获该异常后向页面输出相应的错误信息。
public class UploadServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doPost(request, response);
	}
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		DiskFileItemFactory factory = new DiskFileItemFactory();
		ServletFileUpload upload = new ServletFileUpload(factory);
		// 设置单个上传文件的大小
		upload.setSizeMax(1024 * 10);
		try {
			List<FileItem> items = upload.parseRequest(request);
			for (FileItem fileItem : items) {
				if (fileItem.isFormField()) {
					……
				}else{
					String name = fileItem.getName();
					……
					in.close();
				}
			}
		} catch (FileUploadException e) {
			if (e instanceof FileUploadBase.SizeLimitExceededException) {
				// 在request中保存错误信息
				request.setAttribute("msg", "上传失败!上传的文件超出了10KB!");
				// 转发到index.jsp页面中!在index.jsp页面中需要使用${msg}来显示错误信息
	request.getRequestDispatcher("/index.jsp").forward(request, response);
			}
			e.printStackTrace();
		}
	}
}

文件缓存大小与临时目录

一般情况下,上传文件默认都是先存储在内存中,然后在拷贝到服务器端的磁盘中。但是这样会有一些问题出现,例如单个文件过大时,占用服务器端资源会过多,导致服务器性能变差。这时可以通过手动设置文件缓存大小和上传文件的临时目录来解决。如果不设置上传的文件缓存大小,默认值为10KB,其表示如果上传文件小于10KB的话,会先存储在服务器端的内存中,如果上传文件大小大于10KB的话,会先存储在服务器端默认指定的临时目录中,而上传文件的默认临时目录为System.getProperty("java.io.tmpdir")。

手动修改上传文件缓存大小及临时目录的方式如下:

Ø  手动修改上传文件缓存大小:DiskFileItemFactory.setSizeThreshold(缓存字节数);

Ø  手动修改上传文件临时目录:

DiskFileItemFactory.setRepository(newFile(getServletContext().getRealPath(临时目录相对路径)));

想要删除临时目录下的临时文件的话,只需要调用FileItem的delete()方法即可。

上传文件的缓存文件大小与临时目录:
       * 默认情况下:上传文件的输入流存储在服务器端的内存中(缓存).
       * 问题:
         * 当上传的单个文件过大时,导致服务器端的内存空间不足(性能下降)
       * 解决:
         * 指定上传文件的缓存大小.
* 如果上传文件的缓存大于指定的缓存(内存中)大小,使用临时文件(硬盘中)的方式.
       * 如何实现:
         * 指定上传的缓存大小:
  * factory.setSizeThreshold(1024*1024);
* 设置上传临时目录:
  * factory.setRepository(new File(getServletContext().getRealPath("/tmp")));
  * 机制:当文件上传成功后,删除临时目录中的临时文件.
    fileItem.delete();
     * 上传文件比较大时,上传的速度变慢?
       * 原因就是输入流与输出流的问题.
         * 输出流:new BufferedOutputStream(new FileOutputStream(new File(realPath,fileName)));
* 输入流:new BufferedInputStream(fileItem.getInputStream());

public class UploadServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doPost(request, response);
	}
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		DiskFileItemFactory factory = new DiskFileItemFactory();
		// 内存缓冲区
		factory.setSizeThreshold(3 * 1024 * 1024); // 3M
		// 临时文件位置
		File repository = new File(getServletContext().getRealPath("/tmp"));
		factory.setRepository(repository);
		ServletFileUpload upload = new ServletFileUpload(factory);
		try {
			List<FileItem> items = upload.parseRequest(request);
			for (FileItem fileItem : items) {
				if (fileItem.isFormField()) {
					……
				}else{
					String name = fileItem.getName();
					……
					in.close();
				}
			}
		} catch (FileUploadException e) {
			e.printStackTrace();
		}
	}
}

文件上传进度监听器

目前大部分具有文件上传功能的,在文件上传过程中,可以实时看到上传进度。可以使用ServletFileUpload提供的setProgressListener()方法实现,在客户端配置Ajax技术即可实现。由于目前没有掌握Ajax异步交互技术,所以只能在服务器端完成查看进度功能。

其中除使用setProgressListener()方法实现外,还需要计算如下几个结果:

Ø  已用时间:当前时间 – 开始时间

Ø  速度:已经上传大小/ 已用时间

Ø  剩余大小:总大小 – 已经上传大小

Ø  剩余时间:剩余大小/ 速度

根据上述内容,具体查看上传进度功能如下:

//获取上传文件的开始时间
final long start = System.currentTimeMillis();
//为文件上传对象,帮助查看进度监听器
upload.setProgressListener(new ProgressListener() {
	/*
	 * update(long pBytesRead, long pContentLength, int pItems)
	 *  * 该方法用于获取文件上传过程相关数据内容.
	 *  * 参数pBytesRead:到目前为止已经读取字节总数(已上传大小)
	 *  * 参数pContentLength:正在上传字节总数 (文件大小)
	 *  * 参数pItems:当前上传文件,是表单的第几个元素
	 */
	public void update(long pBytesRead, long pContentLength, int pItems) {
		if(pBytesRead == 0){
			// 刚开始上传,还没有数据
			return;
		}
		// 计算已经使用时间
		long hasUseTime = System.currentTimeMillis() - start;
		// 计算上传速度
		double speed = ((double)pBytesRead) / hasUseTime;
		// 计算剩余大小
		long restBytes = pContentLength - pBytesRead;
		// 计算剩余时间
		long restTime = (long) (restBytes / speed);
		
		System.out.println("已用时间:" + hasUseTime + ", 传输速度:" + speed + ", 剩余时间:" + restTime);
		try {
			Thread.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
});
* 上传文件比较大时,上传的速度变慢?
       * 原因就是输入流与输出流的问题.
         * 输出流:new BufferedOutputStream(new FileOutputStream(new File(realPath,fileName)));
* 输入流:new BufferedInputStream(fileItem.getInputStream());
     * 文件上传的进度条的功能:
       * 服务器端:监听文件上传的整个过程(从开始上传到上传结束)
         * 需要文件上传的监听器.
//为文件上传的过程,增加监听器(监听文件上传的过程)
		upload.setProgressListener(new ProgressListener() {
			/*
			 * update(long pBytesRead, long pContentLength, int pItems)
			 * 作用:用于监听文件上传过程的状态信息
			 * 参数:
			 * pBytesRead:到目前为止,已经读取上传文件的大小
			 * pContentLength:上传文件的总大小
			 * pItems:当前上传文件,是表单的第几个元素
			 * 
			 * 计算以下四个结果:
			 * *已用时间:当前时间 - 开始时间
			 * *上传速度:已经上传大小/已用时间
			 * *剩余大小:总大小 - 已经上传的大小
			 * *剩余时间:剩余大小 /速度
			 */
			@Override
			public void update(long pBytesRead, long pContentLength, int pItems) {
				//获取文件上传的当前时间
				long currentTime = System.currentTimeMillis();
				//已用时间:当前时间 - 开始时间
				long useTime = currentTime - startTime;
				//上传速度:已经上传大小/已用时间
				long speed = pBytesRead / useTime;
				//剩余大小:总大小 - 已经上传的大小
				long restBytes = pContentLength - pBytesRead;
				//剩余时间:剩余大小 /速度
				long restTime = restBytes/speed;
			}
		});
  * 客户端:添加进度条的内容(异步交互技术Ajax)

最终的代码:

package 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.ProgressListener;
import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;
//完成文件上传功能
public class UploadServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		//1.创建文件上传的工厂类实例对象
		DiskFileItemFactory factory = new DiskFileItemFactory();
		//设置上传文件的缓存大小
		factory.setSizeThreshold(1024*1024);
		//设置指定的临时文件存储的目录
		factory.setRepository(new File(getServletContext().getRealPath("/tmp")));
		//2.创建文件上传的servletFileUpload实例对象
		ServletFileUpload upload = new ServletFileUpload(factory);
		
		//获取当前时间,即文件开始上传的时间
		final long startTime = System.currentTimeMillis();
		
		//为文件上传的过程,增加监听器(监听文件上传的过程)
		upload.setProgressListener(new ProgressListener() {
			/*
			 * update(long pBytesRead, long pContentLength, int pItems)
			 * 作用:用于监听文件上传过程的状态信息
			 * 参数:
			 * pBytesRead:到目前为止,已经读取上传文件的大小
			 * pContentLength:上传文件的总大小
			 * pItems:当前上传文件,是表单的第几个元素
			 * 
			 * 计算以下四个结果:
			 * *已用时间:当前时间 - 开始时间
			 * *上传速度:已经上传大小/已用时间
			 * *剩余大小:总大小 - 已经上传的大小
			 * *剩余时间:剩余大小 /速度
			 */
			@Override
			public void update(long pBytesRead, long pContentLength, int pItems) {
				//获取文件上传的当前时间
				long currentTime = System.currentTimeMillis();
				//已用时间:当前时间 - 开始时间
				long useTime = currentTime - startTime;
				//上传速度:已经上传大小/已用时间
				long speed = pBytesRead / useTime;
				//剩余大小:总大小 - 已经上传的大小
				long restBytes = pContentLength - pBytesRead;
				//剩余时间:剩余大小 /速度
				long restTime = restBytes/speed;
			}
		});
		
		
		try {
			
			//限制单个文件上传的大小
			upload.setFileSizeMax(1024*1024*3);//3M
			//限制上传文件的总大小
			upload.setSizeMax(1024*1024*10);
			
			
			//3.利用ServletFileUpload实例对象解析request对象中有关文件上传的内容(文件上传的内容都封装在了request对象中了)
			List<FileItem> items = upload.parseRequest(request);
			//4.遍历文件上传到的list集合,得到有关文件上传到的所有内容(普通字段和上传内容)
			for (FileItem fileItem : items) {
				/*
				 * 如何可以知道当前的FileItem是普通项还是文件项
				 * *通过FileItem.isFormField()方法判断当前是普通项还是文件项
				 * *结果是true的话,表示当前是普通项
				 * *结果是false的话,表示当前是文件项
				 */
				if(fileItem.isFormField()){
					//普通项
					/*getFieldName():获取普通项的name属性值
					 *getString(): 获取普通项的文本内容
					 */
					String fieldName = fileItem.getFieldName();
					
					String fieldValue = fileItem.getString();
					System.out.println(fieldName+":"+fieldValue); //filetext : readme
				}else{
					//文件项
					/*getName():获取文件项的上传文件名称
					 *getInputStream(): 获取文件项的上传文件输入流
					 */
					String fileName = fileItem.getName();
					//解决浏览器过旧的问题:上传文件名的处理 :C:\Users\JYL\Desktop\day0106\笔记\readme.txt
					int index = fileName.lastIndexOf("\\");
					if(index>=0){
						fileName.substring(index+1);
					}
					//fileName.substring(fileName.lastIndexOf("\\")+1);
					//为了防止文件同名,给文件名增加一个唯一的id
					fileName = UUID.randomUUID().toString()+fileName;
					System.out.println(fileName);//readme.txt
					InputStream in = fileItem.getInputStream();
					
					//将上传目录进行分级处理
					int hashcode = fileName.hashCode();
					//生成一级目录
					int d1 = hashcode & 0xf;
					//生成二级目录
					int d2 = (hashcode >>> 4) & 0xf;
					
					
					
					
					String realPath = getServletContext().getRealPath("/WEB-INF/uploads/"+d1+"/"+d2);
					new File(realPath).mkdirs();
					OutputStream out = new FileOutputStream(new File(realPath,fileName));
					
					IOUtils.copy(in, out);
					
					in.close();
					out.close();
					
				}
			}
		} catch (Exception e) {
			if(e instanceof FileSizeLimitExceededException){
				System.out.println("你上传的文件过大,请压缩后再上传");
			}
			if(e instanceof SizeLimitExceededException){
				System.out.println("你上传的文件总大小过大,请压缩后再上传");
			}
		}

	}

	
	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}

}



扩展:浏览器内核产品不同(不建议使用IE)
       * IE浏览器:IE6.0\7.0 IE8.0\9.0IE10\11
       * 其他浏览器:Webkit(苹果)
         * 苹果浏览器
* 火狐浏览器:自主内核产品.
* 谷歌浏览器:自主内核产品.
       * 众多国内浏览器:
         * 百度浏览器:号称自主内核.
* 腾讯浏览器:号称自主内核.
* 遨游浏览器:号称自主内核.
* 360\搜狗\猎豹等...



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值