Java web文件上传下载
2016-07-27 19:37
435 查看
[版权申明:本文系作者原创,转载请注明出处]
文章出处:http://blog.csdn.net/sdksdk0/article/details/52048666
作者:朱培 ID:sdksdk0 邮箱: zhupei@tianfang1314.cn
本文主要从javaweb上传文件到服务器中,并且在服务器端进行数据文件存储,主要分享了文件上传原理、使用第三方开源工具进行上传以及一些文件上传时需要注意的地方,文件的优化处理,还有简易分享了从我们刚才上传进去的文件进行下载。需要掌握基本的开发流程,底层实现原理等。
form表单的enctype必须是multipart/form-data
提供input type=”file”
application/x-www-form-urlencoded(默认):
正文:name=aa&password=123
服务器获取数据:request.getParameter(“name”);
基于html form表单上传的数据都是以类似—————————–7da3c8e180752{0x130x10}这样的分割符来标记一块数据的起止。
文件上传的Content-Type为multipart/form-data; boundary=—-WebKitFormBoundaryhQslmBE7nbTLTJzD,而普通的form表单的Content-Type为application/x-www-form-urlencoded。因此,我们可以利用HttpServletRequest的request.getHeaderNames()方法和request.getHeaders(headName)方法得到请求头Headers中的Content-Type数据,然后根据Content-Type数据中是否包含multipart/form-data来区分请求是否为文件上传请求。其中boundary为文件数据的分隔符,用于区分上传多个文件。
1、界面
我们就是需要一个form表单,为其添加enctype属性和post方法:
2、逻辑处理
我们在一个servlet中进行处理:
//创建DiskFileItemFactory对象
DiskFileItemFactory factory=new DiskFileItemFactory();
接下来就是分为两种情况了,一种是对普通的表单元素进行处理,另一种是对文件类的数据进行处理,对于第一种情况的话就比较简单,我们直接获取名字就可以了,基本上不用过多的处理。
对于第二种情况,需要我们直接对上传文件进行一系列的处理了,我们首先需要在服务器上找一个存放文件的地方,然后截取上传的文件名、构建输出流、关闭流等操作。
2、让文件名唯一。
3、避免同一个文件夹中的文件过多。
3.1按照日期进行存储。
3.2用文件名的hashCode计算需要进行存储的目录,二级目录。
4、限制文件的大小。web方式不适合上传大的文件。
4.1单个文件大小:
4.2总文件大小:多文件上传
5、限制文件的上传类型。
5.1通过文件扩展名来进行限制。
5.2通过文件MIME类型来限制。
6、空文件上传解决方案。
判断文件名是否为空,当文件名为空时return。
7、临时文件
DiskFileItemFactory的作用是产生FileItem对象。其内部有一个缓存,默认大写拾10kb,如果上传文件超过10kb,则用磁盘作为缓存。存放缓存的目录默认是系统的临时目录。
如果是自己用IO流实现的文件上传,则需要在流关闭后,清理临时文件。
可以使用FileItem.write(File f)实现文件上传的保存。
8、中文编码
9、动态js控制上传框
新建一个list.jsp文件:
要记得引入jstl的核心包 。
接下做界面显示的servlet。
我们之前上传的文件是用GUID做过相应处理的,是一个拼接好的文件名,用来防止文件同名的情况发生,这里用户浏览到的文件当然要和上传的时候的文件名 相同,所以这里我们要进行截取,把GUID拼接的前缀去掉,以“_”分开。一个是在服务器中防同名的文件名以及显示出来的截取后的文件名。这里以前面说过的,防同名文件方法2:用文件名的hashCode计算需要进行存储的目录,二级目录。这里也就需要使用hashCode找到做过文件的路径。
接下来就是下载的处理了。
通过这个网址进行访问
http://localhost:8080/UploadDemo/servlet/ShowAllFilesServlet
源码下载:https://github.com/sdksdk0/UploadDemo
文章出处:http://blog.csdn.net/sdksdk0/article/details/52048666
作者:朱培 ID:sdksdk0 邮箱: zhupei@tianfang1314.cn
本文主要从javaweb上传文件到服务器中,并且在服务器端进行数据文件存储,主要分享了文件上传原理、使用第三方开源工具进行上传以及一些文件上传时需要注意的地方,文件的优化处理,还有简易分享了从我们刚才上传进去的文件进行下载。需要掌握基本的开发流程,底层实现原理等。
一、文件上传原理
提供form表单,method必须是postform表单的enctype必须是multipart/form-data
提供input type=”file”
Enctype属性
告知服务器请求正文的MIME类型。application/x-www-form-urlencoded(默认):
正文:name=aa&password=123
服务器获取数据:request.getParameter(“name”);
文件上传原理:
解析请求正文的每部分的内容。基于html form表单上传的数据都是以类似—————————–7da3c8e180752{0x130x10}这样的分割符来标记一块数据的起止。
文件上传的Content-Type为multipart/form-data; boundary=—-WebKitFormBoundaryhQslmBE7nbTLTJzD,而普通的form表单的Content-Type为application/x-www-form-urlencoded。因此,我们可以利用HttpServletRequest的request.getHeaderNames()方法和request.getHeaders(headName)方法得到请求头Headers中的Content-Type数据,然后根据Content-Type数据中是否包含multipart/form-data来区分请求是否为文件上传请求。其中boundary为文件数据的分隔符,用于区分上传多个文件。
二、使用第三方工具实现文件上传
fileupload组件工作流程:
开发步骤
导入commons-fileupload.jar、commons-io.jar包。1、界面
我们就是需要一个form表单,为其添加enctype属性和post方法:
<form action="${pageContext.request.contextPath}/servlet/UploadServlet2" method="post" enctype="multipart/form-data" > 姓名: <input type="text" name="name" /><br /> 照片: <input type="file" name="photo" /><br /> <input type="submit" value="提交" /> </form>
2、逻辑处理
我们在一个servlet中进行处理:
request.setCharacterEncoding("UTF-8"); //判断用户的请求内容是不是multipart/form-data boolean isMultipart=ServletFileUpload.isMultipartContent(request); if(!isMultipart){ throw new RuntimeException("error!"); }
//创建DiskFileItemFactory对象
DiskFileItemFactory factory=new DiskFileItemFactory();
//创建核心解析类ServlertFileUpload ServletFileUpload sfu=new ServletFileUpload(factory); //解析请求对象 List<FileItem> items=new ArrayList<FileItem>(0); try { items=sfu.parseRequest(request); } catch (FileUploadException e) { // TODO Auto-generated catch block e.printStackTrace(); } for(FileItem item:items){ if(item.isFormField()){ processFormField(item); }else{ processUploadField(item); } } response.getWriter().write("sucess!");
接下来就是分为两种情况了,一种是对普通的表单元素进行处理,另一种是对文件类的数据进行处理,对于第一种情况的话就比较简单,我们直接获取名字就可以了,基本上不用过多的处理。
private void processFormField(FileItem item) { String fieldName=item.getFieldName(); String fieldValue=item.getString(); System.out.println(fieldValue+"="+fieldName); }
对于第二种情况,需要我们直接对上传文件进行一系列的处理了,我们首先需要在服务器上找一个存放文件的地方,然后截取上传的文件名、构建输出流、关闭流等操作。
private void processUploadField(FileItem item) { try { InputStream in=item.getInputStream(); String filename=item.getName(); //在服务器上找一个存放文件的地方 String storeDirectoryRealPath=getServletContext().getRealPath("/WEB-INF/files"); File storeDirectory=new File(storeDirectoryRealPath); if(!storeDirectory.exists()){ storeDirectory.mkdirs(); } //截取上传的文件名 //filename=filename.substring(filename.lastIndexOf(File.separator)+1); if(filename!=null){ filename=FilenameUtils.getName(filename); } String guidFilename=GUIDUtil.generateGUID()+"_"+filename; //按日期来区分存储目录 // String childDirectory=makeChileDirectory(storeDirectory); String childDirectory=makeChildDirectory(storeDirectory,guidFilename); //构建输出流 OutputStream out=new FileOutputStream(new File(storeDirectory,childDirectory+File.separator+guidFilename)); int len = -1; byte buf[] = new byte[1024]; while((len=in.read(buf))!=-1){ out.write(buf, 0, len); } in.close(); out.close(); } catch (IOException e) { e.printStackTrace(); } }
三、文件上传优化处理
1、把保存的文件放在用户无法直接访问到的地方:例如放在:在WEB-INF/files目录中。String storeDirectoryRealPath=getServletContext().getRealPath("/WEB-INF/files");
2、让文件名唯一。
String guidFilename=GUIDUtil.generateGUID()+"_"+filename; //构建输出流 OutputStream out=new FileOutputStream(new File(storeDirectory,guidFilename));
3、避免同一个文件夹中的文件过多。
3.1按照日期进行存储。
String childDirectory=makeChileDirectory(storeDirectory); private String makeChileDirectory(File storeDirectory) { Date now=new Date(); DateFormat df=new SimpleDateFormat("yyyy-MM-dd"); String sdate=df.format(now); File f=new File(storeDirectory,sdate); if(!f.exists()){ f.mkdirs(); } return sdate; }
3.2用文件名的hashCode计算需要进行存储的目录,二级目录。
private String makeChildDirectory(File storeDirectory, String guidFilename) { int hashCode = guidFilename.hashCode(); int dir1 = hashCode&0xf;// 0~15 int dir2 = (hashCode&0xf0)>>4;//0~15 String s = dir1+File.separator+dir2; File f = new File(storeDirectory,s); if(!f.exists()){ f.mkdirs(); } return s; }
4、限制文件的大小。web方式不适合上传大的文件。
4.1单个文件大小:
ServletFileUpload sfu=new ServletFileUpload(factory); sfu.setFileSizeMax(4*1024*1024);//限制不超过4M
4.2总文件大小:多文件上传
ServletFileUpload sfu=new ServletFileUpload(factory); sfu.setSizeMax(8*1024*1024);//总文件大小
5、限制文件的上传类型。
5.1通过文件扩展名来进行限制。
String extensionName=FilenameUtils.getExtension(filename);
5.2通过文件MIME类型来限制。
String mimeType=item.getContentType();
6、空文件上传解决方案。
判断文件名是否为空,当文件名为空时return。
7、临时文件
DiskFileItemFactory的作用是产生FileItem对象。其内部有一个缓存,默认大写拾10kb,如果上传文件超过10kb,则用磁盘作为缓存。存放缓存的目录默认是系统的临时目录。
DiskFileItemFactory factory=new DiskFileItemFactory(); //更改临时文件的存放目录 factory.setRepository(new File("D:/"));
如果是自己用IO流实现的文件上传,则需要在流关闭后,清理临时文件。
FileItem.delete();
可以使用FileItem.write(File f)实现文件上传的保存。
8、中文编码
request.setCharacterEncoding("UTF-8"); //该编码要和jsp页面保持一致 String fieldValue=item.getString("UTF-8");
9、动态js控制上传框
<form action="${pageContext.request.contextPath}/servlet/UploadServlet3" method="post" enctype="multipart/form-data"> name:<input type="text" name="name"/><br/> <div id="d1"> <div> photo:<input type="file" name="photo"/><input type="button" value="继续上传" onclick="addFile()"/> </div> </div> <input type="submit" value="上传"/> </form> <script type="text/javascript"> function addFile(){ var d1 = document.getElementById("d1"); var oldInnerHtml = d1.innerHTML; d1.innerHTML=oldInnerHtml+"<div>photo:<input type='file' name='photo'/><input type='button' value='删除' onclick='deleteOne(this)'/></div>"; } function deleteOne(delBtn){ delBtn.parentNode.parentNode.removeChild(delBtn.parentNode); } </script>
四、文件的下载
首先我们来看一下页面的处理:新建一个list.jsp文件:
全部资源如下:<br /> <c:forEach items="${map}" var="me"> <c:url value="/servlet/DownLoadServlet" var="url"> <c:param name="filename" value="${me.key}"></c:param> </c:url> ${me.value} <a href="${url}">下载</a><br/> </c:forEach>
要记得引入jstl的核心包 。
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
接下做界面显示的servlet。
我们之前上传的文件是用GUID做过相应处理的,是一个拼接好的文件名,用来防止文件同名的情况发生,这里用户浏览到的文件当然要和上传的时候的文件名 相同,所以这里我们要进行截取,把GUID拼接的前缀去掉,以“_”分开。一个是在服务器中防同名的文件名以及显示出来的截取后的文件名。这里以前面说过的,防同名文件方法2:用文件名的hashCode计算需要进行存储的目录,二级目录。这里也就需要使用hashCode找到做过文件的路径。
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //key:GUID文件名 value:old文件名 Map<String, String> map = new HashMap<String, String>(); //获取/WEB-INF/files的真实路径 String rootDirectoryRealPath = getServletContext().getRealPath("/WEB-INF/files"); //递归遍历找出所有的文件 System.out.println(rootDirectoryRealPath); File rootDirectory = new File(rootDirectoryRealPath); treeWalk(rootDirectory,map); //存到请求范围中,转发给jsp显示 request.setAttribute("map", map); request.getRequestDispatcher("/list.jsp").forward(request, response); } //递归遍历找出所有的文件,把文件名高出来 public void treeWalk(File file, Map<String, String> map) { if(file.isFile()){ String guidFileName = file.getName(); String oldFileName = guidFileName.substring(guidFileName.indexOf("_")+1); map.put(guidFileName, oldFileName); }else{ //目录 File[] childFiles = file.listFiles(); for(File f:childFiles){ treeWalk(f, map); } } }
接下来就是下载的处理了。
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String guidFilename = request.getParameter("filename");//get方式提交的 guidFilename = new String(guidFilename.getBytes("ISO-8859-1"),"UTF-8"); //计算存放路径 File storeDirectory = new File(getServletContext().getRealPath("/WEB-INF/files")); String childDirectory = makeChildDirecotry(storeDirectory, guidFilename);// 13/1 //构建输入流 InputStream in = new FileInputStream(new File(storeDirectory,childDirectory+File.separator+guidFilename)); //用响应对象的输出流输出:下载的方式 String oldFileName = guidFilename.substring(guidFilename.indexOf("_")+1); response.setHeader("Content-Disposition", "attachment;filename="+URLEncoder.encode(oldFileName,"UTF-8"));//不适用火狐 response.setContentType("application/octet-stream"); OutputStream out = response.getOutputStream(); int len = -1; byte buf[] = new byte[1024]; while((len=in.read(buf))!=-1){ out.write(buf, 0, len); } in.close(); } private String makeChildDirecotry(File storeDirectory, String guidFilename) { int hashCode = guidFilename.hashCode(); int dir1 = hashCode&0xf; int dir2 = (hashCode&0xf0)>>4; String s = dir1+File.separator+dir2; File f = new File(storeDirectory,s); if(!f.exists()){ f.mkdirs(); } return s; }
通过这个网址进行访问
http://localhost:8080/UploadDemo/servlet/ShowAllFilesServlet
源码下载:https://github.com/sdksdk0/UploadDemo
相关文章推荐
- Java web文件上传下载
- Java web文件上传下载
- java基础之:匿名内部类
- Java心得体会之常量
- JDBC进行处理大文件和批处理
- ACM中java解题常用方法及准则
- Two Sum
- 实现java中自定义类加载器
- Java主要特性
- CXF与Spring集成
- Java环境变量配置(详细)
- SpringMVC中无法获取SessionFactory的解决!
- DAO层、Service层、Controller层、View层介绍
- Spring 国际化支持 消息传递 以及 AOP 面向方面编程
- java中用小数点分隔字符串问题
- SpringMVC+MyBatis+logback
- Java 设计模式
- Spring Boot使用Druid和监控配置
- Spring-AOP:简介
- javaweb学习总结(十九)——JSP标签