* 必须添加文件表单项:
2、上传对Servelt的限制
* 首先以前获取表单的数据使用String str=request.getParametere(“xxx”),但是当表单的属性为enctype=”multipart/form-data”
他就作废了,永远返回null
** 那如何获取表单的数据呢?
使用request.getInputStream(),返回的是ServletInputStream对象,“这个对象包含了整个请求的体。”
3、什么是多部件表单?
表单中的每个表单选项都是一个部件,一个部件中含有自己的请求头、空行、请求体。其中包含了普通表单项和文件表单项
* 普通表单项
》一个头: Content-Disposition: 包含name=”xxx”属性,表示表单项的名称
》请求体;就是表单项的值
* 文件表单项
》2个头
* Content-disposition:包含了name属性(表单项的名称),还有filename属性(文件的名称)
* Content-Type:它是上传文件的MIME类型,例如:image/pjep,表示上传的是图片,图片中jpg是扩展名
3、分割ServletInputStream获得多部件中的内容(commons-filupload.jar)
*所依赖的jar包
commons-filupload.jar,依赖commons-io.jar这个组件
* 上传操作三步走
相关类:
工厂: DiskFileItemFactory
解析器:ServletFileUpload
表单项:FileItem
1)、创建工厂:
DiskFileItemFactory factory=new DiskFileItemFactory();
2)、创建解析器
ServletFileUpload sfu=new ServletFileUpload(factory);
3)、使用解析器来解析request:
List fileItemList= sfu.paraseRequest(request);
* 操作类FileItem中的常用方法
针对普通表单项:
** boolean isFormField():是否是普通表单项,
** String getFieldName():返回当前表项的名称
** String getString(String charset):返回表单项的值
针对文件表单项:
** String getName():返回上传的文件名称
** long getSize():返回上传文件的字节数
** InputStream getInputStream():返回上传文件对应的输入流
** void write(File desFile):把上传的文件内容保存到指定的文件中。
上传的细节:
1、文件必须保存到WEB-INF下,目的是为了不让浏览器直接访问
通常我们会在WEB-INF目录下创建一个uploads目录,用来存放上传的文件,而在Servlet中找到这个目标需要使用ServletContext的getReadPath(String)方法, 例如:
String savepath=servletContext.getRealPath("/WEB-INF/uploads");
savepath为:F:\tomcat7.0\webapps\upload\WEB-INF\uploads
2、有的浏览器上传的文件时绝对路径
我们对绝对路径不感兴趣,所以我们需要切割路径。
int index=filename.getLastIndexof(“\\”);
if(index !=-1){
filename=filename.substring(index+1);
}
3、文件名乱码或者普通表单项乱码
普通字段的乱码:
1、手动转换
request.setCharactorEncoding(“utrf-8”);
2、FileItem.getString("UTF-8"); //获得名称的时候传一个字符集
上传文件的乱码:
ServletFileUpload.setHeaderEncoding("UTF-8");
4、文件名同名问题:我们需要为每个文件添加名称前缀,这个前缀要保证不能重复。
方法:
为文件名加不重复的前缀( CommonUtils.uuid() )
Filename=CommonUtils.uuid()+”_”+filename;
5 一个目录不能存放过多的文件(存在目标打散)
一般存放1000个文件就是上限,如果多,那么打开目录时就会很“卡”。打散的方法有很多,常见的有日期打散算法,首字母打散算法。
日期打散算法:每天生成一个目录,但也有文件过多的情况。
首字母打散算法:如果文件名是中文,也会导致目录过多。
哈希打散(最长用的方法):
* 通过文件名称得到int值(hashCode())
* 将得到的int值转换成16进制(Integer.toHex)
* 用着16进制的前两位创建目录
代码演示:
request.setCharacterEncoding(“utf-8”);
response.setContentType(“text/html;charset=’utf-8’”);
DiskFileItemFactory factory =new DiskFileItemFactory();
ServletFileUpload sfu=new ServletFileUpload(factory);
try {
List listItem=sfu.parseRequest(request);
FileItem f1=listItem.get(1);//得到文件表单部件
String root=this.getServletContext().getRealPath("/WEB-INF/uploads");
String filename = f1.getName();
int index = filename.lastIndexOf("\\"); //解决绝对路径问题
if(index!=-1){
filename=filename.substring(index+1);
}
String savename=CommonUtils.uuid()+"_"+filename; //解决同名问题(依赖itacast-tool)
//3、得到savagename的hashcode的int值,将int转换成16进制
String hex = Integer.toHexString(savename.hashCode());
//4、获取hex的前两个字母,与root连接在一起,构成一个完成的路径
File file = new File(root, hex.charAt(0)+"/"+hex.charAt(1));
//5、创建目录链和文件
file.mkdirs();
File desfile=new File(file,savename);
//6、保存
f1.write(desfile);
} catch (FileUploadException e) {
new RuntimeException(e);
} catch (Exception e) {
new RuntimeException(e);
}
注意:
Itcast-tool包依赖两个包commons-beanutils和commons-logging包
6、上传文件的大小限制
* 单个文件大小限制(限制一个文件表单项 )
> ServletFileUpload 对象sfu
sfu.setFileSizeMax(100*1024); //限制大小为100KB
> 如果上传的文件超出限制,在parseRequest()方法执行时,会抛FileUploadBase.FileSizeLimitException异常
* 整个请求的大小限制
> ServletFileUpload 对象sfu
sfu.setSizeMax(1024*1024); //限制大小为1M
>如果上传的文件超出限制,在parseRequest()方法执行时,会抛出
FileUploadBase.SizeLimitException异常
* 所以可以通过异常的类别来区分到底是那种异常,从而给出响应的错误信息
例:
DiskFileItemFactory factory=new DiskFileItemFactory();
ServletFileUpload sfu=new ServletFileUpload(factory);
sfu.setSizeMax(1024*1024); //设置整个表单的大小为1M
try{
List
list=sfu.paraseRequest(request);
}catch(FileUploadException e){
If(e instanceof FileUploadBase.SizeLimitException){
//设置错误信息
request.setAttribute(“mstr”,”您上传的文件超出了100KB”);
request.getRequestDiapatcher(“/index.jsp”).forward(request,response);
}
}
注意:
限制文件的大小的设置一定要在解析reque对象之前,否则就会报错。
7、缓存大小与临时目录
一个JVM启动默认的堆内存大小是64M。当文件过于太大时,要先保存在缓存中。当保存文件时,只需将缓存中的大文件直接copy到目标地址就行了
问题1:超出多大时,才向硬盘保存,而不保存在内存。(默认是10KB):缓存大小
问题2:向硬盘的什么目录保存(答:临时目录)
问题3:如何设置缓存大小和临时目录?
使用DisFileItemFactory的构造函数,
DisFileItemFactory(int sizeThreshold , File repository)
int sizeThreshold:指定缓存大小
File repository:指定临时目录
在上传文件时,会创建临时目录。待临时目录上传后临时目录就会删除。
下载就是我们响应客户端的是字节数据,把一个文件变成字节数组,使用response.getOutputStream()来响应浏览器。
下载的要求?
形象的表示:(两个头一个流)
Content-Type:传递给客户端的文件是什么MIME类型
Content-Disposition:默认值时inline,表示在浏览器窗口打开,主要的作用是:让浏览器给我们提供下载下载框。通过设置 attachment;
filename=xxx(attachment意味增加一个附件,filename指定的是下载的内容)。
代码演示:
//文件的MIMe类型
String filename="G:/CloudMusic/走马.mp3";
FileInputStream fis=new FileInputStream(filename); //一流
String mimeType = this.getServletContext().getMimeType(filename);//得到MiMe类型
String disposition="attachment;filename=zouma.mp3";
//设置二头
response.setHeader("Content-Type", mimeType);
response.setHeader("Content-Disposition", disposition);
ServletOutputStream output = response.getOutputStream();
IOUtils.copy(fis, output);
注意;
/index.jsp 中的路径时是在url访问中使用的,不要搞错。
文件下载指定文件名的中文乱码问题?
因为在firfox中,使用的是Base64编码。而在已IE为首的绝大多数浏览器时是以URL编码的。
一种解决方法;
filename=new String(filename.getBytes(“GBK”),”ISO-8859-1”);
这种方法虽然能解决但是对于有些特殊字符还是会出错的。
Eg:
filename=new String(“走马”.getBytes(“GBK”),”ISO-8859-1”);
另一种方法(常用):只需传进filename和request对象就行(记住这个函数就行了)
使用一个函数方法,不需要记住但是会使用就行。(需要commons-Io包)
public static String filenameEncoding(String filename ,HttpServletRequest request ,)throw IOException{
String agent=request.getHeader(“User-Agent”); //获得浏览器
If(agent.contains(“Firefox”)){
BASE64Encoding base64Encoding=new BASE64Encoding();
filename=”=?utf-8?B”+base64Encoder.encode(filename.getBytes(“utf-8”))+”?=”;
}else if(agent.contains(“MSIE”)){
filename=URLEncoder.encode(filename,”utf-8”);
}else{
Filename=URLEncoder.encode(filename,”utf-8”);
}
return filename;
}