第九章上传下载及其他

来源:互联网 发布:多组数据的显著性差异 编辑:程序博客网 时间: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不重要,只是帮大家理解原理

 



0 0
原创粉丝点击