文件上传和下载
文件上传概述 1、上传对表单限制 *method="post" *enctype="multipart/form-data" *表单中需要添加文件表单项:<input type="file" name="xxx" /> 2、上传对Servlet限制 *request.getParameter("xxx");这个方法在表单为enctype="multipart/form-data"时,它作废了。它永远都返回null。 *ServletInputStream request.getInputStream();返回整个请求的体 -------------------------------------- 多部件表单的体 1、每隔出多个部件,即一个表单项一个部件 2、一个部件中自己包含请求头和空行,以及请求体 3、普通表单项 >一个头:Content-Disposition: 包含name="xxx",即表单项名称。 >体就是表单项的值 4、文件表单项: >两个头: *Content-Disposition:包含name="xxx",即表单项名称;还有一个filename,表示上传文件的名称 *Content-Type:它是上传文件的MIME类型,例如:image/pjepg,表示上传的是图片。图片中jpg拓展名的图片 >体就是上传文件的内容。 ================================ commons-fileupload *commins-fileupload.jar *commons-io.jar 这个小组件,它会帮我们解析request中的上传数据,解析后的结果是一个表单项数据封装到一个FileItem对象中。我们只需要调用FIleItem的方法即可! --------------------------- 1、上传三步 相关类: *工厂:DiskFileItemFactory *解析器:ServletFileUpload *表单项:FileItem 1)。创建工厂:DiskFileItemFactory factory=new DiskFileItemFactory(); 2)。创建解析器:ServletFileUpload sfu=new ServletFileUpload(factory); 3)。使用解析器来解析request,得到FileItem集合:List<FileItem> files=suf.parseRequest(request); 2.FileItem *boolean isFormField(): 是否为普通表单选项,返回true为是,否则为文件表单项 *String getFieldName(): 返回当前表单项的名称 *String getString(String charset):返回表单项的值,不适用于文件表单项 *String getName():返回上传的文件名称 *long getSize():返回上传文件的字节数 *InputStream getInputStream():返回上传文件对应的输入流 *void write(File file):把上传的文件内容保存到指定的文件中 *String getContentType():获取文件类型 ---------------------------------- 上传的细节: 1.文件必须保存在WEB-INF下 *目的是不让浏览器直接访问到! *把文件保存到WEB-INF目录下! 2.文件名称问题 *有的浏览器上传的文件名是绝对路径,这需要切割! c:\files\baibing.jpg String filename=fi2.getName(); index=filename.lastIndexOf("\\"); if(index!=-1){ filename=filename.substring(index+1); } *文件名乱码或者普通表单项乱码:request.setCharacterEncoding("utf-8");因为fileupload内部会调用request.getCharacterEncoding(); >request.setCharacterEncoding("utf-8"); 优先级低 >ServletfileUpload.setHeaderEncoding("utf-8"); *文件同名问题:我们需要为每个文件添加名称前缀,这个前缀应该保证不能重复 >filename=CommonUtils.uuid()+"_"+filename 3.目录打散 *不能再一个目录下存放过多文件。影响速度 >首字母打散:使用文件的首字母作为目录名称。例如abc.txt,我们保存在a目录下,如果a目录不存在,则自动创建并且保存在里面 >时间打散:使用当前日期做为目录。 >哈希打散: *通过文件名称得到int值,即调用hashCode() *把int值转换为16进制0~9,A~F *获取16进制的前两位用来生成目录,目录为两层! 4.上传文件的大小限制 *单个文件大小限制 >sfu.setFileSizeMax(100*1024);:限制单个文件大小为100k >上面的方法调用,必须在解析器进行解析之前 >如果大小超出限制,解析器在parseRequest抛出异常FileUploadBase.FileSizeLimitExceededException *整个请求所有数据大小限制 >sfu.setSize(1024*1024);限制整个表单大小为1M >这个方法也必须在解析器开始解析之前调用 >如果超出限制,解析器在parseRequest抛出异常FileUploadBase.SizeLimitExceededException 5.缓存大小与临时目录 *上传文件的步骤一般是先保存在内存再保存到硬盘上。但是内存大小会限制,所以先保存在硬盘上,然后你写入硬盘保存位置的时候从临时文件复制,然后删除临时文件 *缓存大小:超出多大才向硬盘保存!默认为10kb *临时目录:向硬盘的什么目录保存 设置缓存大小与临时目录:new DiskFileItemFactory(20*1024,new File("E:\\f\\temp")
package cn.itcast.web.servlet; import java.io.File; import java.io.IOException; 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.FileUploadBase; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import cn.itcast.utils.CommonUtils; public class Upload3Servlet extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); // DiskFileItemFactory factory=new DiskFileItemFactory(); DiskFileItemFactory factory=new DiskFileItemFactory(20*1024, new File("E:\\f\\temp")); ServletFileUpload upload=new ServletFileUpload(factory); // upload.setFileSizeMax(100*1024);//限制单个文件大小为100k // upload.setSizeMax(1024*1024);//限制整个表单大小为1M // System.out.println(1); try { List<FileItem> list=upload.parseRequest(request); FileItem fi=list.get(1); String root=this.getServletContext().getRealPath("/WEB-INF/files"); /* * 生成二级目录 * 1)得到文件名称 * 2)得到hashCode * 3)转化成16进制 * 4)获取前2个字符用来生成目录 */ String filename=fi.getName(); /* * 处理文件名的绝对路径问题 */ int index=filename.lastIndexOf("\\"); if(index!=-1){ filename=filename.substring(index+1); } /* * 给文件添加yyid前缀,处理同名问题 */ String savename=CommonUtils.uuid()+"_"+filename; /* * 得到hashCode */ int hCode=filename.hashCode(); String hex=Integer.toHexString(hCode); /* * 获取hex的前两个字母,与root连接在一起,生成一个完整的路径 */ File dirFile=new File(root,hex.charAt(0)+"/"+hex.charAt(1)); /* * 创建目录链 */ dirFile.mkdirs(); /* * 创建目录文件 */ File destFile=new File(dirFile,savename); /* * 保存 */ fi.write(destFile); } catch (FileUploadException e) { if(e instanceof FileUploadBase.FileSizeLimitExceededException){ request.setAttribute("msg", "您上传的文件超出了100kb"); request.getRequestDispatcher("/form3.jsp").forward(request, response); }else if(e instanceof FileUploadBase.SizeLimitExceededException){ request.setAttribute("msg", "您上传的的总大小超出了1M"); System.out.println(2); request.getRequestDispatcher("/form3.jsp").forward(request, response); } } catch (Exception e) { e.printStackTrace(); } } }
下载
1.下载就是向客户端响应字节数据!
原来我们响应的都是html的字符数据!
把一个文件变成字节数组,使用response.getOutputStream()来响应给浏览器
2.下载的要求
*两个头一个流
>Content-Type:你传递给客户端的文件时什么类型。例如:image/pjpeg
*通过文件名称调用ServletContext的getMimeType方法得到MIME类型
>Content-Disposition:它的默认值为inline,表示在浏览器窗口中打开!下载使用:attachment:filename=xxx
*在filename=后面跟随的是显示在下载框中的名称
>流:要下载的文件数据!
------------------------------
下载的细节
1.显示在下载框中的中文名称时,会出现乱码
*FireFox:Base64编码
*其他大部分浏览器:URL编码。
通用方案:filename=new String(filename.getByte("GBK"),"ISO-8859-1") //个别字符不太好使
package cn.itcast.web.servlet; import java.io.FileInputStream; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import cn.itcast.tool.utils.DownLoadUtils; public class Download1Servlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /* * 两个头一个流 * Content-Type * Content-Disposition * 流:下载文件的数据 */ String filename="E:\\KuGou\\陈粒 - 奇妙能力歌.mp3"; String framename=DownLoadUtils.filenameEncoding("陈粒-奇妙能力歌.mp3", request); String ContentType=this.getServletContext().getMimeType(filename);//通过文件名获取文件MIME // String ContentDisposition="attachment;filename=a.mp3"; // String ContentDisposition="attachment;filename="+new String("陈粒-奇妙能力歌.mp3".getBytes("GBK"),"ISO-8859-1"); String ContentDisposition="attachment;filename="+framename; // System.out.println(new String("陈粒-奇妙能力歌.mp3".getBytes("GBK"),"ISO-8859-1"));//????-?????????è.mp3 我们的控制台乱码,但是浏览器喜欢这个 /* Returns the MIME type of the specified file, or null if the MIME type is not known. The MIME type is determined by the configuration of the servlet container, and may be specified in a web application deployment descriptor. Common MIME types are "text/html" and "image/gif". */ FileInputStream input=new FileInputStream(filename); response.setHeader("Content-Type",ContentType); response.setHeader("Content-Disposition", ContentDisposition); //获取绑定了客户端的流 ServletOutputStream output=response.getOutputStream(); IOUtils.copy(input, output); input.close(); } } package cn.itcast.tool.utils; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import javax.servlet.http.HttpServletRequest; import sun.misc.BASE64Encoder; public class DownLoadUtils { public static String filenameEncoding(String filename,HttpServletRequest request) throws UnsupportedEncodingException{ String agent=request.getHeader("User-Agent");//获取浏览器 if(agent.contains("FireFox")){ BASE64Encoder base64Encoder=new BASE64Encoder(); 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; } }
使用base64编码和解码:
package cn.itcast.base64; import java.io.IOException; import org.junit.Test; import sun.misc.BASE64Encoder; import sun.misc.BASE64Decoder; public class Demo1 { @Test public void fun1() throws IOException { // BASE64编码 String s = "Username"; BASE64Encoder encoder = new BASE64Encoder(); s = encoder.encode(s.getBytes("UTF-8")); System.out.println(s); // BASE64解码 BASE64Decoder decoder = new BASE64Decoder(); byte[] bytes = decoder.decodeBuffer("dXNlcm5hbWU6"); System.out.println(new String(bytes, "UTF-8")); } }