第九章上传下载及其他

原创 2016年08月29日 18:28:22

第九章上传下载及其他

本章主干知识点:

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
{
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException
	{
		this.doPost(req, resp);
	}
	
	@Override
	protected 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,即使是普通的表单域,也是一个表单域对应一个DiskFileItem
				
				DiskFileItem 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开始的,所以要加1
			int 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
{
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException
	{
		this.doPost(req, resp);
	}

	@Override
	protected 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不重要,只是帮大家理解原理

 



相关文章推荐

Android和FTP服务器交互,上传下载文件(实例demo)

今天同学说他备份了联系人的数据放在一个文件里,想把它存到服务器上,以便之后可以进行下载恢复..于是帮他写了个上传,下载文件的demo 主要是 跟FTP服务器打交道~因为这个东东有免费的可以身亲哈 ...

Qt之FTP的上传下载(代码实现)

FTP服务器实现是我整个智能监控系统的一部分,所以在这里记录。 实现并不是很难,大家一起学习看看。 我用的是腾讯云,在此感谢腾讯的校园计划。 整个UI就是两个按钮,一个上传,一个下载。 看看界...

Linux 终端访问 FTP 及 上传下载 文件

分类: Linux 2010-08-17 17:58 25443人阅读 评论(0) 收藏 举报 linux终端useroraclecommandftp服务器   今天同事问我一...
  • cqhweb
  • cqhweb
  • 2014年05月26日 11:01
  • 9504

python实现的ftp自动上传下载程序(支持目录递归操作)----转

转自 : http://www.cppblog.com/fwxjj/archive/2011/12/14/162085.html 因为python脚本可以直接用文本工具打开修改,所以没有设置...

ABAP文件上传下载 用SMW0

T-CODE: SMW0 在这里只介绍二进制数据,HTML模板的上传也一样。 另外也可以用CBO TABLE管理文件 可以看我另一个博文:CBO TABLE管理文件上传下载 选择 二进制...
  • pely122
  • pely122
  • 2012年11月08日 10:06
  • 3828

Mac使用rz、sz远程上传下载文件

习惯了 在windows下的securecrt和xshell的rz 和sz。 rz 可以很方便的从客户端传文件到服务器,sz也可以很方便的从服务器传文件到客户端,就算中间隔着跳板机也不影响。在mac...

.Net Core 图片文件上传下载

当下.Net Core项目可是如雨后春笋一般发展起来,作为.Net大军中的一员,我热忱地拥抱了.Net Core并且积极使用其进行业务的开发,我们先介绍下.Net Core项目下实现文件上传下载接口。...

jspSmartUpload上传下载全攻略(转)

一、安装篇 jspSmartUpload是由www.jspsmart.com网站开发的一个可免费使用的全功能的文件上传下载组件,适于嵌入执行上传下载操作的JSP文件中。该组件有以下几个特点: 1、使用...

JAVA中使用FTPClient实现文件上传下载

在JAVA程序中,经常需要和FTP打交道,比如向FTP服务器上传文件、下载文件,本文简单介绍如何利用jakarta commons中的FTPClient(在 commons-net包中)实现...
  • hj7jay
  • hj7jay
  • 2016年04月06日 09:06
  • 2296

由response.setContentType()方法开始谈JSP/Servelt上传下载文件

文章非原创,参考链接见文末! 常见的MIME类型如下表: 序号 内容类型 文件扩展名 描述 1 applicatio...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:第九章上传下载及其他
举报原因:
原因补充:

(最多只允许输入30个字)