第九章上传下载及其他
来源:互联网 发布:多组数据的显著性差异 编辑:程序博客网 时间:2024/06/16 20:05
第九章上传下载及其他
本章主干知识点:
1、上传,上传的安全性问题;下载;
2、forward和include;虚拟路径;
第 1 节1-文件上传简介
第 2 节2-Commons-fileupload使用1
第 3 节3-Commons-fileupload使用2
第 4 节4-Commons-fileupload使用3
第 5 节5-案例:上传文件到日期文件夹下
第 6 节6-案例:限制上传文件类型
第 7 节7-关于上传的高级话题
第 8 节8-文件的下载
第 9 节9-forward和include
第 10 节10-Servlet的虚拟路径
第 11 节11-fn函数及其他
【第 1 节1-文件上传简介】
文件上传
如果要进行文件上传,则需要采用method="post",并且要设定enctype="multipart/form-data"。multipart的报文的特点。
不能使用get,不能不写enctype,否则只上传文件名
【第 2 节2-Commons-fileupload使用1】
Servlet没有内置对文件上传的支持,要用第三方库,一般用Commons的fileupload组件,同时引入commons-io。代码备注
【commons-fileupload-1.2.2.jar】【commons-io-2.1.jar】
【Upload1Servlet.java】
//临时文件夹File tempDir = new File(req.getSession().getServletContext().getRealPath("/WEB-INF/temp"));//第一个参数为单个文件最大大小DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory(4 * 1024 * 1024,tempDir);ServletFileUpload fileUpload = new ServletFileUpload(diskFileItemFactory);List<DiskFileItem> files = fileUpload.parseRequest(req);DiskFileItem file = files.get(0);if(file.getSize()<=0){resp.getWriter().println("必须选择一个文件");return;}String fileName = file.getName();String fileExt = FilenameUtils.getExtension(fileName).toLowerCase();if(!fileExt.equals("jpeg")&&!fileExt.equals("jpg")&&!fileExt.equals("png")){resp.getWriter().println("只能上传图片");return;}File destFile = new File(req.getSession().getServletContext().getRealPath("upload/"+fileName));FileOutputStream osDest = new FileOutputStream(destFile);IOUtils.copy(file.getInputStream(), osDest);IOUtils.closeQuietly(osDest);
【第 3 节3-Commons-fileupload使用2】
使用enctype="multipart/form-data"之后就不能用req.getParameter获取普通表单参数了;普通表单项也被当成了DiskFileItem,通过getString()获得值。可以根据req.getContentType()是否以="multipart/form-data"开头来判断是什么提交方式。封装一个根据参数名字获取DiskFileItem的方法。
【RupengUtils.java】
添加方法/** * 从files找到fieldName为fieldName的DiskFileItem * @param files * @param fieldName * @return 如果没找到就返回null */public static DiskFileItem findDiskFileItem(List<DiskFileItem> files,String fieldName){for(DiskFileItem file : files){if(file.getFieldName().equals(fieldName)){return file;}}return null;}
【第 4 节4-Commons-fileupload使用3】
【第 5 节5-案例:上传文件到日期文件夹下】
【第 6 节6-案例:限制上传文件类型】
案例:将用户上传的文件保存到项目的upload文件夹的“年/月/日”文件夹下,只允许上传不大于2MB的zip、rar、jpg、gif、png文件。
【第 7 节7-关于上传的高级话题】
注意:不能获得浏览器端的全路径,因为没有意义;只能让用户手动选择文件,不能通过代码给上传空间赋值,因为不安全;可以弄多个type="file"的input,但是不能选择文件夹;ajax不能上传文件。
【Upload1Servlet.java】
package com.rupeng.web7;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.PrintWriter;import java.io.Writer;import java.util.Calendar;import java.util.Date;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.FileUploadException;import org.apache.commons.fileupload.disk.DiskFileItem;import org.apache.commons.fileupload.disk.DiskFileItemFactory;import org.apache.commons.fileupload.servlet.ServletFileUpload;import org.apache.commons.io.FilenameUtils;import org.apache.commons.io.IOUtils;public class Upload1Servlet extends HttpServlet{@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException{this.doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException{req.setCharacterEncoding("UTF-8");//设置正确的编码之后才可以避免DiskFileItem文件名乱码的情况resp.setCharacterEncoding("UTF-8");resp.setContentType("text/html;charset=UTF-8");//对于enctype="multipart/form-data"的表单,不能使用req.getParameter("action")来获得表单的值//无论是type="file"还是普通表单域(input、select、textarea),都是一项对应一个DiskFileItem//对于普通表单域getFieldName()获得的是表单域的名字,getString()获得的是值//对于type="file" getFieldName()获得的是表单域的名字,getName()获得的是用户选择的文件名//经过试验得知,如果表单是enctype="multipart/form-data",那么请求的Content-Type就是"multipart/form-data *****"//所以通过request的Content-Type就可以知道浏览器端到底是不是enctype="multipart/form-data"String action=null;List<DiskFileItem> files=null;//if(req.getContentType().startsWith("multipart/form-data"))//判断请求的contentType得知浏览器端form的encType是不是"multipart/form-data"if(ServletFileUpload.isMultipartContent(req)){String tempFullPath = this.getServletContext().getRealPath("/WEB-INF/temp");File fileTemp = new File(tempFullPath);//上传临时文件夹DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory(2 * 1024 * 1024,fileTemp);ServletFileUpload upload = new ServletFileUpload(diskFileItemFactory);PrintWriter out = resp.getWriter();try{files = upload.parseRequest(req);//从请求中解析出上传的 文件内容//对于是enctype="multipart/form-data"的form,即使是普通的表单域,也是一个表单域对应一个DiskFileItemDiskFileItem actionDiskItem = RupengUtils.findDiskFileItem(files, "action");if(actionDiskItem!=null){action = actionDiskItem.getString();//取得action表单的值}} catch (FileUploadException e){resp.getWriter().print("文件上传错误"+e.getMessage());}}else{action = req.getParameter("action");}resp.setCharacterEncoding("UTF-8");resp.setContentType("text/html;charset=UTF-8");//String action = req.getParameter("action");if(RupengUtils.isNullOrEmpty(action)){req.getRequestDispatcher("/Upload1.jsp").forward(req, resp);}//一旦使用enctype="multipart/form-data",那么服务器端就不能用req.getParameter("action")获得表单数据了else if(action.equals("uploadSubmit")){//因为一旦执行一次parseRequest之后request的流的指针就指向最后了,所以一般不重复parseRequest//就是用之前已经parseRequest完成的结果List<DiskFileItem> files=null;DiskFileItem f1 = RupengUtils.findDiskFileItem(files, "f1");String fileExt = FilenameUtils.getExtension(f1.getName());//拿到文件的后缀if(!fileExt.equalsIgnoreCase("zip")&&!fileExt.equalsIgnoreCase("rar")&&!fileExt.equalsIgnoreCase("jpg")&&!fileExt.equalsIgnoreCase("png")&&!fileExt.equalsIgnoreCase("gif")){resp.getWriter().print("不允许上传.zip、.jpg、.rar、.png、.gif之外的文件类型");return;}//f1.getInputStream()//文件内容//上传文件全路径//String uploadFullPath = this.getServletContext().getRealPath("/upload/"+f1.getName());//获得当前时间,拿到年月日 toString()。Date now = new Date(System.currentTimeMillis());//当前时间Calendar calendar = Calendar.getInstance();int year = calendar.get(Calendar.YEAR);int month = calendar.get(Calendar.MONTH)+1;//月是从0开始的,所以要加1int day = calendar.get(Calendar.DAY_OF_MONTH);//以年月日为文件夹String uploadFullPath = this.getServletContext().getRealPath("/upload/"+year+"/"+month+"/"+day+"/"+f1.getName());//如果文件夹不存在则FileOutputStream会报错//因此要先创建文件夹File file = new File(uploadFullPath);//文件对象 File parentFile = file.getParentFile();//拿到所在的文件夹if(!parentFile.exists()){parentFile.mkdirs();//mkdirs会把不存在的父文件夹递归创建}FileOutputStream fos = new FileOutputStream(uploadFullPath);//Eclipse中的upload是开发时候的文件夹,tomcat运行的时候其实操作的是.me_tcat\webapps\这个文件夹下的try{//f1.getInputStream()上传文件的内容流IOUtils.copy(f1.getInputStream(), fos);//把上传文件的内容流拷贝到输出的文件流中}finally{IOUtils.closeQuietly(fos);}resp.getWriter().print("上传完成");DiskFileItem f2 = RupengUtils.findDiskFileItem(files, "f2");if(f2.getSize()<=0)//判断用户有没有选择文件,即使用户没有选择,f2也不是null,只是getSize()==0{resp.getWriter().print("用户没选择f2");}else{resp.getWriter().print("f2文件名"+f2.getName());}}}}
【第 8 节8-文件的下载】
对于“Servlet”右键另存为其实还是浏览器向服务器发http请求,然后把服务器运行后返回的http报文体保存到文件中。不会把Servlet的源代码下载下来。
如何弹出保存提示框。增加报文头:resp.addHeader("Content-Disposition","attachment;filename="+URLEncoder.encode("动态文件.txt","Utf-8"));不同浏览器行为不一样
【index.jsp】
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%@page import="java.io.File"%><%@page import="java.io.FileOutputStream"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> </head> <body> <a href="download1">click</a> </body></html>
【Download1Servlet.java】
package com.rupeng.web7;import java.io.IOException;import java.net.URLEncoder;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class Download1Servlet extends HttpServlet{@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException{this.doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException{resp.setContentType("text/html;charset=UTF-8");resp.setCharacterEncoding("UTF-8");req.setCharacterEncoding("UTF-8");resp.addHeader("Content-Disposition", "attachment;filename="+URLEncoder.encode("过年好.htm","UTF-8"));resp.getWriter().print("hello!<br/>");resp.getWriter().print("hello!<br/>");resp.getWriter().print("hello!<br/>");}}
JavaWeb 其他
【第 9 节9-forward和include】
Servlet响应结果导航
使用response.sendRedirect(url)方式告诉浏览器重新请求url资源。浏览器参与了,所以浏览器地址栏会变。
使用请求的转发request.getRequestDispatcher(path).forward(request,response)方式把请求转发到另一个Servlet,让另一个Servlet去进一步处理请求并生成响应(path不带项目路径)。服务器的事情,因此浏览器地址栏不会变。path可以是servlet、jsp甚至可以是静态html
使用请求的包含request.getRequestDispatcher(path).include(request,response),在服务器内部执行path这个请求,并且把请求的结果拼接到当前请求中。和forward对比:servlet1、servlet2中分别输出111、222,然后分别对比forward和include的区别
resp.getWriter().println("1111111");
req.getRequestDispatcher("/servlet2").include(req,resp);
jsp的两种include(*)
l <%@ include file="xxx.jsp"%> 这个是说明标签。查看Catalina文件夹(访问一次才会生成java),发现它是把被include的文件和当前文件合并成一个java文件了。所以在两个jsp中不能定义重名的变量,可以写一下,访问一下,然后看生成的java里面就有两个重名的变量了。
l <jsp:include page="xxx.jsp"/> 是把2.jsp运行时include,把2.jsp的执行结果include到1.jsp 中,因此两个页面中有重名变量也没关系。
l include的用途:一个网站中很多页面相同的部分抽象出来。
【第 10 节10-Servlet的虚拟路径 】
HttpServlet虚拟路径
由于每个HttpServlet都是服务器的一个虚拟资源,资源都是可以访问可以定位的,所以每个HttpServlet都有一个或多个虚拟路径,浏览器在请求时,请求的都是资源的路径
有三种方式配置HttpServlet的虚拟路径
全路径匹配 /hello /aa/hello /xxx /aaa /aa/bb/cc
目录匹配 /* (匹配所有请求) /aa/* (匹配/aa/下的所有请求)
后缀名匹配 *.do (匹配所有以.do结尾的请求) *.action 不能有路径、避免常用静态文件的后缀。
优先级 : 全路径 > 目录 > 后缀名
一个HttpServlet可配置多个虚拟路径:多个servlet-mapping对应一个servlet
【第 11 节11-fn函数及其他】
fn:函数(*)
JSTL中内置了几个函数,主要和字符串相关。偶尔会用到
1、<%@taglibprefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
2、<c:iftest="${fn:contains(username,'admin')}">
用户名不能包含敏感词
</c:if>
3、aa<c:outvalue="${fn:trim(username)}"/>bb
4、长度:${fn:length(username)}
5、${fn:join(names,"|")}names是一个字符串数组
其他:fn:startsWith、fn:endsWith、fn:escapeXml、fn:indexOf、fn:replace、fn:split、fn:substring、fn:toLowerCase、fn:toUpperCase等
百度“JSTL fn 函数”
还允许自定义函数,用的更少
【Fn1.jsp】
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>函数</title></head><body><c:if test="${fn:contains(name3,name2)}">含有<br/></c:if><c:if test="${fn:contains(name3,'tor')}">含有tor<br/></c:if>111<c:out value="${fn:trim(name)}"></c:out>222<br/>${fn:length(name3)} <br/>${fn:join(names,",") }</body></html>
Servlet线程安全问题
由于服务器只会创建一个Servlet处理所有的特定请求,所以Servlet类中定义的字段是多个线程共享的,就会有线程安全问题
解决方法:
不在Servlet类中定义字段(成员变量)、只使用局部变量,就自然没有的多线程问题,也推荐这么做
如果非要在Servlet中定义字段,就需要自己手动的控制线程同步
后续导学
自定义Tag、Filter、Listener不重要,只是帮大家理解原理
- 第九章上传下载及其他
- 网格及其他
- 软件、技术及其他
- 古拉格及其他
- 菜单及其他资源
- 前途、后路及其他
- 《墨攻》及其他
- 墨攻及其他
- 北京,年会及其他
- 面试、工作及其他
- 搜狐博客及其他
- 查全率、SEO及其他
- 线程安全及其他
- 安家,javascript,及其他
- 农历及其他
- db2 游标及其他
- 良知、职业道德及其他
- >读后感及其他
- AVL树插入删除
- .Net框架搭建之2、SQL Server MEF依赖注入 MVC Repository框架
- 如何使用fiddler2拦截移动端app的数组请求
- 适配——Drawable适配,占用内存测试
- Unity ScreenSpaceShadowMask Blur
- 第九章上传下载及其他
- 字符串转整数
- Android学习分享-常见控件02-Button详解
- MongoDB和MySQL性能测试及其结果分析
- 极客班STL第二周学习笔记
- Lua的值与类型
- Android动画使用(二)
- (C#)最大公共子串
- iOS开源库Carthage提交自己的开源库