jsp文件上传原理

文件上传的原理,文件上传用到的jar包,文件上传遇到文件编码问题,文件的大小限制,文件上传临时文件的设置,文件上传重命名问题,分目录存储上传的文件,多个文件上传时,没有上传内容的问题,上传进度检测。

一、文件上传的原理

1、文件上传的前提:
a、form表单的method必须是post
b、form表单的enctype必须是multipart/form-data(决定了POST请求方式,请求正文的数据类型)
注意:当表单的enctype是multipart/form-data,传统的获取请求参数的方法失效。

请求正文:(MIME协议进行描述的,正文是多部分组成的)
-----------------------------7dd32c39803b2
Content-Disposition: form-data; name="username"


wzhting
-----------------------------7dd32c39803b2
Content-Disposition: form-data; name="f1"; filename="C:\Documents and Settings\wzhting\妗岄潰\a.txt"
Content-Type: text/plain


aaaaaaaaaaaaaaaaaa
-----------------------------7dd32c39803b2
Content-Disposition: form-data; name="f2"; filename="C:\Documents and Settings\wzhting\妗岄潰\b.txt"
Content-Type: text/plain


bbbbbbbbbbbbbbbbbbb
-----------------------------7dd32c39803b2--



c、form中提供input的type是file类型的文件上传域

注:在这里大家就可以看到,上传的文件名是乱码,下面会解决该问题的。


二、利用第三方组件实现文件上传
1、commons-fileupload组件:
jar:commons-fileupload.jar  commons-io.jar(这两个包会传到我的资源 页:http://download.csdn.net/detail/mr_li13/9202423)
2、核心类或接口
DiskFileItemFactory:设置环境
public void setSizeThreshold(int?sizeThreshold) :设置缓冲区大小。默认是10Kb。
当上传的文件超出了缓冲区大小,fileupload组件将使用临时文件缓存上传文件
public void setRepository(java.io.File repository):设置临时文件的存放目录。默认是系统的临时文件存放目录。

ServletFileUpload:核心上传类(主要作用:解析请求的正文内容)
boolean isMultipartContent(HttpServletRequest?request):判断用户的表单的enctype是否是multipart/form-data类型的。
List parseRequest(HttpServletRequest request):解析请求正文中的内容
setFileSizeMax(4*1024*1024);//设置单个上传文件的大小
upload.setSizeMax(6*1024*1024);//设置总文件大小
FileItem:代表表单中的一个输入域。
boolean isFormField():是否是普通字段
String getFieldName:获取普通字段的字段名
String getString():获取普通字段的值

InputStream getInputStream():获取上传字段的输入流
String getName():获取上传的文件名

三、文件上传中要注意的9个问题
1、如何保证服务器的安全
把保存上传文件的目录放到WEB-INF目录中。
2、中文乱码问题
2.1普通字段的中文请求参数
String value = FileItem.getString("UTF-8");
2.2上传的文件名是中文
解决办法:request.setCharacterEncoding("UTF-8");
3、重名文件被覆盖的问题
System.currentMillions()+"_"+a.txt(乐观)

UUID+"_"+a.txt:保证文件名唯一
4、分目录存储上传的文件
方式一:当前日期建立一个文件夹,当前上传的文件都放到此文件夹中。
方式二:利用文件名的hash码打散目录来存储。
int hashCode = fileName.hashCode();

1001 1010 1101 0010 1101 1100 1101 1010
hashCode&0xf;   0000 0000 0000 0000 0000 0000 0000 1111 &
---------------------------------------------
0000 0000 0000 0000 0000 0000 0000 1010   取hashCode的后4位
0000~1111:整数0~15共16个

1001 1010 1101 0010 1101 1100 1101 1010
(hashCode&0xf0) 0000 0000 0000 0000 0000 0000 1111 0000  &
--------------------------------------------
0000 0000 0000 0000 0000 0000 1101 0000  >>4
--------------------------------------------
0000 0000 0000 0000 0000 0000 0000 1101
0000~1111:整数0~15共16个
5、限制用户上传的文件类型
通过判断文件的扩展名来限制是不可取的。

通过判断其Mime类型才靠谱。

String mimetype=FileItem.getContentType();

if(mimetype.startsWith("image")){//只允许上传照片,其他类型也可以

6、如何限制用户上传文件的大小
6.1单个文件大小限制。超出了大小友好提示
抓异常进行提示: org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException
6.2总文件大小限制。超出了大小友好提示

抓异常进行提示:org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException


异常最好放入catch中,以便可以进行提醒

7、临时文件的问题
commons-fileupload组件不会删除超出缓存的临时文件。

FileItem.delete()方法删除临时文件。但一定要在关闭流之后。

8、多个文件上传时,没有上传内容的问题
if(fileName==null||"".equals(fileName.trim())){
continue;
}
9、上传进度检测
给ServletFileUpload注册一个进度监听器即可,把上传进度传递给页面去显示
//pBytesRead:当前以读取到的字节数
//pContentLength:文件的长度
//pItems:第几项
public void update(long pBytesRead, long pContentLength,
int pItems) {
System.out.println("已读取:"+pBytesRead+",文件大小:"+pContentLength+",第几项:"+pItems);

}



案例:

1、目录

                             

2.jsp页面

    <form action="${pageContext.request.contextPath}/servlet/UploadServlet1" method="post" enctype="multipart/form-data">
    	用户名:<input type="text" name="username"/><br/>
    	文件1:<input type="file" name="f1"/><br/>
    	文件2:<input type="file" name="f2"/><br/>
    	<input type="submit" value="保存"/>
    </form>	

3.servlet类

package com.dp.web.controller;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
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.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
//入门案例
public class UploadServlet1 extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//解决中文编码的问题
		request.setCharacterEncoding("UTF-8");//这个只能解决文件名的中文问题
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter out=response.getWriter();
		try {
			//得到存放上传文件的真实路径(为了保证安全放在WEB-INF目录下)
			String storePath = getServletContext().getRealPath("/WEB-INF/file");
			
			//设置环境
			DiskFileItemFactory factory = new DiskFileItemFactory();
			//设置临时文件目录
			factory.setRepository(new File(getServletContext().getRealPath("/temp")));
			//判断一下form是否是enctype=multipart/form-data类型的
			boolean isMultipart = ServletFileUpload.isMultipartContent(request);
			if(!isMultipart){
				System.out.println("大傻鸟");
				return;
			}
			//ServletFileUpload核心类
			ServletFileUpload upload = new ServletFileUpload(factory);
			
			//限制文件大小
			upload.setFileSizeMax(4*1024*1024);//最大4M
			//限制总上传文件
			upload.setSizeMax(8*1024*1024);//最多6M
			//上传进度条设置
			upload.setProgressListener(new ProgressListener() {
				//pBytesRead:当前读取到的字节数
				//pContentLength:文件的长度
				//pItems:第几项
				@Override
				public void update(long pBytesRead, long pContentLength,
						int pItems) {
					  System.out.println("已经读取字节:"+pBytesRead+"文件长度为:"+pContentLength+"第"+pItems+"项");
				}
				
				
			});
			
			//解析
			List<FileItem> items = upload.parseRequest(request);
			for(FileItem item:items){
				if(item.isFormField()){
					//普通字段
					String fieldName = item.getFieldName();
					String fieldValue = item.getString("UTF-8");//这里解决请求参数的中文
					System.out.println(fieldName+"="+fieldValue);
				}else{
					//得到文件的mime类型
					String mimetype=item.getContentType();
				//	if(mimetype.startsWith("image")){//只允许上传照片
					//上传字段
					InputStream in = item.getInputStream();
					//上传的文件名
					String fileName = item.getName();//   C:\Documents and Settings\wzhting\妗岄潰\a.txt        a.txt
					if(fileName==null||"".equals(fileName.trim())){
						continue;//加入只有一个文件上传,并不全部上传的话
					}
					fileName = fileName.substring(fileName.lastIndexOf("\\")+1);//   a.txt
					fileName=UUID.randomUUID()+"_"+fileName;//解决重名文件问题
					//构建输出流
					//新建打散存储目录
					String newStorPath=makeStorPath(storePath,fileName);//创建一个新路径
					String storeFile = newStorPath+"\\"+fileName;
					OutputStream out1 = new FileOutputStream(storeFile);
					
					byte b[] = new byte[1024];
					int len = -1;
					while((len=in.read(b))!=-1){
						out1.write(b,0,len);
					}
					out.close();
					in.close();
					item.delete();//删除临时文件
					}
				//}
			}
		}catch(org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException e){ //这是文件大小超出异常
			out.write("单个文件大小不能超出4M!");
			
		}catch(org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException e){ //这是文件大小超出异常
			out.write("总文件大小不能超出6M!");
			
		}catch (Exception e) {
			e.printStackTrace();
		}
		
	}

	private String makeStorPath(String storePath, String fileName) {
		int hasCode=fileName.hashCode();
		int dir1=hasCode&0xf;
		int dir2=hasCode&0xf0;
		String path=storePath+"\\"+dir1+"\\"+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);
	}

}

注意:最后提醒,上传成功后,在web中刷新不出来的,文件存在tomcate下的webapps的你的项目下,上传成功后可以打开看看。





  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值