您的位置:首页 > 其它

【HM】第12课:文件上传&文件下载&注解

2015-07-09 23:09 543 查看
<pre>

day12

上节内容回顾
1、jstl的标签
*if choose
*forEach
<c:forEachvar="l" items="${list}">

2、jsp开发模式
*模型一
*模型二(mvc模式)
**mvc模式
***m:模型,使用javabean
***v:视图,使用jsp
***c:控制器,使用servlet

*dao模式:数据访问对象,专注于对数据库的操作
**首先创建接口,在接口里面定义操作数据库的方法
**其次,创建类实现这个接口里面的方法

*javaee三层结构:web层、业务逻辑层、持久化层

3、使用mvc+dao模式案例
思维导图

1、文件上传
(1)什么是文件上传:把本地的文件上传到服务器上,比如网盘应用
(2)实现文件上传,在servlet里面没有实现上传的技术,要想实现文件的上传使用到第三方的技术。

(3)使用的上传的技术常用的包含两种:
第一种:jspsmartupload:适用于模型一,嵌入JSP中的组件,会有Java代码。
第二种:fileupload:适用于模型二(mvc),
FileUpload是 Apache commons下面的一个子项目,组件FileUpload依赖于Commons IO组件

*使用fileUpload组件需要导入jar包(两个jar包)

(4)如何实现文件的上传?
三个基本要求
第一个要求:在页面中需要有一个表单,表单的提交方式需要是post方式
第二个要求:在表单里面,需要有一个文件上传项,在文件上传项里面必须要有name属性
<inputtype="file" name="filename"/>
第三个要求:在form表单里面,设置一个属性enctype的值 multipart/form-data
关于enctype属性在一般情况下不需要做任何的设置,只是在做文件上传的时候才需要使用到这个属性
=<form action="/day12/upload" method="post"enctype="multipart/form-data">

*创建一个页面,在页面里面写表单,表单里面写普通输入项和文件上传项,提交到一个servlet里面,查看执行过程。

(5)使用代码实现文件的上传(使用fileUpload组件实现文件的上传)
*实现上传的步骤是固定的
第一步:创建一个磁盘文件项工厂
**使用DiskFileItemFactory类,new DiskFileItemFactory()创建工厂
第二步:创建一个核心的上传类
**使用ServletFileUpload类,new ServletFileUpload(FileItemFactory fileItemFactory)
第三步:使用核心上传类解析request对象
**使用ServletFileUpload类里面的方法parseRequest(javax.servlet.http.HttpServletRequest request)
**返回的是list集合,在集合里面有泛型是FileItem,
***在提交的表单里面,可能有普通输入项和文件上传项,会有多个,每个都是使用FileItem,多个项构成
了一个list集合
第四步:返回list集合,判断普通输入项还是文件上传项
**遍历list集合,判断普通输入项还是文件上传项 isFormField()
第五步:如果是普通输入项,获取输入的值;如果是文件上传项,写上传的代码

**代码
//得到上传文件的名称
Stringfilename = fileItem.getName();
//使用getName方法得到文件名称,在某些浏览器下面得到的带路径地址 c:\test\1.txt
intlens = filename.lastIndexOf("\\");
//判断
if(lens!= -1) {
//截取的操作
filename= filename.substring(lens+1);
}
//把表单提交过来的文件写到服务器路径下面
//使用输入流得到提交过来的文件
InputStreamin = fileItem.getInputStream();
//得到upload的完全路径
Stringpath = getServletContext().getRealPath("/upload");
OutputStreamout = new FileOutputStream(path+"/"+filename);
//流对接
intlen = 0;
byte[]b = new byte[1024];
while((len=in.read(b))!=-1){
out.write(b,0, len);
}
in.close();
out.close();

(6)核心api的使用
*DiskFileItemFactory:磁盘文件项工厂
**构造方法
=DiskFileItemFactory()
*设置缓冲区的大小和临时文件存储路径
==设置缓冲区的大小:setSizeThreshold(int sizeThreshold)
==临时文件存放的路径:setRepository(java.io.File repository)

=DiskFileItemFactory(int sizeThreshold, java.io.File repository)
*第一个参数:上传时候有缓冲区,设置缓冲区的大小(单位字节)
*第二个参数:上传文件超过缓冲区的大小,产生临时文件,临时文件存放的路径

*ServletFileUpload:核心上传类
=parseRequest(javax.servlet.http.HttpServletRequest request):返回list集合,list集合的泛型是FileItem
=setFileSizeMax(long fileSizeMax):设置单个上传文件的大小(单位字节)
=setSizeMax(long sizeMax):设置所有上传文件的总的大小
*当上传的文件超过设置的大小,抛出异常:The field filename exceeds its maximum permitted size of 1048576 characters.

=setHeaderEncoding(java.lang.String encoding):设置上传文件名称中文编码

*FileItem
**是一个接口
**方法
=getFieldName():获取表单输入项的name的值,返回string类型
=getString():获取普通输入项的值
=getString(java.lang.String encoding):获取普通输入项的值,解决输入项里面中文的乱码
*参数:设置输入值的编码
=boolean isFormField():判断是普通项还是文件上传项,返回true表示是普通项,返回false表示是文件上传项
=getInputStream() :获取通过表单提交过来的文件的输入流

=String getName():获取上传的文件的名称
*在某些浏览器下面,得到带里面的文件比如c:\text/1.txt
*想要的具体的文件名,截取字符串
=delete():当上传文件超过缓冲区大小产生临时文件,删除临时文件

(7)上传过程中产生的问题
*现在上传了一个文件名称1.txt,第二次再上传一个相同名称的文件 1.txt
*最后上传的文件会把之前的文件覆盖

*解决方法:
**在上传的文件名称里面添加一个随机的唯一的字符串
***如何生成随机的字符串 UUID
//生成随机字符串
Stringuuid = UUID.randomUUID().toString();
filename= uuid+"_"+filename;

**了解:根据文件的内容,计算文件内容的md5值,把文件的md5值存到数据库里面。
第二次上传文件,首先得到文件的md5值,拿着md5的值到数据库查看是否有相同md5,如果有,表示文件内容的相同,
,不需要再保存一份文件,直接返回文件名称。

2、文件下载
(1)什么是文件的下载:从服务器上把文件存到本地的硬盘
(2)如何实现文件的下载
*有两种方式
第一种:使用超链接实现下载
**创建一个页面,在页面上创建一个超链接,超链接的地址要下载的文件的完全路径
***使用超链接这种方式不好,不是所有的格式的文件都提示下载,如果图片的格式,直接打开

第二种:写java代码实现下载(****)
*实现步骤:
第零步:设置要下载的文件的mime类型浏览器
**String type = getServletContext().getMimeType(filename);
response.setContentType(type);
第一步:设置头信息Content-Disposition(无论什么格式都会提示下载)
第二步:使用输入流得到要下载的文件
**InputStream in = new FileInputStream(path);
第三步:使用输出流把文件流写到浏览器
**输出流 response.getOutputStream();
第四步:流对接,最后关闭流

(3)如果要下载的文件名称包含中文,会乱码问题
*如果想要得到当前请求的浏览器:使用头信息 User-Agent,request.getHeader("User-Agent");
*不同的浏览器需要不同的编码,ie采用url编码,火狐采用base64编码
*判断不同的浏览器,不同的浏览器采用不同的处理方式

=============================================================
上午内容的总结
(1)掌握如何使用代码实现文件的上传
*上传的三个要求
*上传步骤
*文件重名问题的解决方法

(2)掌握如何使用代码实现文件的下载

==================================================================

(3)如果要下载的文件名称包含中文,会乱码问题
*如果想要得到当前请求的浏览器:使用头信息 User-Agent,request.getHeader("User-Agent");
*不同的浏览器需要不同的编码,ie采用url编码,火狐采用base64编码
*判断不同的浏览器,不同的浏览器采用不同的处理方式

解决方法:
首先,得到请求的浏览器的类型 User-Agent
其次,根据火狐和ie进行不同的处理
=代码
//得到当前的请求的浏览器的类型
Stringagent = request.getHeader("User-Agent");
//区别不同的浏览器
if(agent.contains("Firefox")){//如果是火狐浏览器
//火狐采用的base64编码
filename= "=?UTF-8?B?"+
new BASE64Encoder().encode(filename.getBytes("utf-8"))+"?=";
}else {
//ie采用的url编码
filename= URLEncoder.encode(filename, "utf-8");
}

3、练习一:使用js控制多文件的上传
*画图分析实现的效果
*需求的描述:
**创建一个页面,在页面上创建两个按钮,一个是上传,一个是增加
**每次点击增加按钮,在按钮下面添加一个文件上传项和删除按钮
**点击每次生成的删除按钮,把删除按钮所在的那一行删除
**点击上传按钮,会把所有文件上传项里面的文件传递服务器

=代码
//增加文件上传项和删除按钮<input type="file" name="filename"/><inputtype="button" value="删除"/>
function add1() {
//得到div
var div1 =document.getElementById("div1");
div1.innerHTML +="<div><input type='file' name='filename'/><inputtype='button' value='删除' onclick='deldiv(this);'/></div>";
}

//删除一行文件上传项和删除按钮
function deldiv(who) {
//最终目的,把删除按钮所在的div删除就可以了
//得到删除按钮的父节点 div
var div11 = who.parentNode;
//把div11删除就可以了,但是要通过父节点删除
var div1 = div11.parentNode;
//使用div1删除div11
div1.removeChild(div11);
}

4、注解
(1)什么是注解:注解是jdk5.0新特性

*jdk5.0里面的新特性:泛型、枚举、增强for循环、可变参数
**增强for循环:底层是使用迭代器实现的
**枚举:在一定范围内,每次只能出现其中的一个
***枚举的定义:使用一个关键字 enum 类名称 { RED,GREEN; }
**可变参数:可变参数只能使用在方法的参数列表中
publicvoid test11(int...args) {}
可变参数理解为数组

*注解就是代码里面特殊的标记
*语法 @注解的名称

(2)jdk里面的注解有三个注解
第一个注解:@Override,限定重写父类的方法名称
在jdk1.6开始,支持接口和实现类的 使用 @Override

第二个注解:@SuppressWarnings("all"),抑制警告

第三个注解:@Deprecated,表示方法过时

(3)自定义注解
*定义方式: @interface 自定义注解的名称 {
注解上面的属性: 类型 名称();
}
*注解上面的属性的类型:基本数据类型、string、Class、注解、枚举以及这些的一维数组

*在方法上面使用:
=代码
@myanno(aa="bbbb",m=1,clazz=TestAnno02.class,my=@my1,c1=Color.RED,arr={"cc","dd"})
publicvoid run1() {}
=代码
@interface myanno {
//定义string类型的属性
Stringaa();
//定义一个int类型的属性
intm();
//定义Date类型的属性
/*
* Invalid type Date for the annotationattribute myanno.date;
* only primitive type, String, Class,annotation, enumeration are permitted or 1-dimensional arrays thereof
* */
//Datedate();
//Class类
Classclazz();
//注解
my1my();
//枚举的属性
Colorc1();
//数组的属性
String[]arr();
}

(4)特殊的注解
*定义注解的属性时候,如果属性的名称是value,表示特殊的注解
*使用这个注解的时候,属性的名称可以不写

=代码
//创建自定义注解
@interfaceMy11 {
Stringvalue();
}

@My11("aa")
publicvoid test1() {}

5、内容补充:JUnit单元测试
(1)测试:单元测试、集成测试、系统测试
**压力测试、性能测试。。。。
(2)什么是单元测试:针对的是一个类里面的某个方法(测试方法的)
*JUnit就是一个单元测试的框架,不需要写main方法,可以直接运行类里面的某个方法
*junit不是jdk的一部分,使用需要导包,但是myeclipse工具自带包,可以直接导入

(3)使用junit单元测试
*使用注解的方式进行测试,如果要某个方法去执行,在这个方法上面添加一个注解 @Test
*运行这个方法:选中这个方法,右键点击run as--> junit test,可以运行这个方法
**运行之后,出现一个绿色条,表示测试通过了
**运行之后,出现一个红棕色条,表示测试没有通过

*如果类里面有多个方法要进行单元测试,点击类里面任意的位置,run as--> junit test,
可以把类里面添加了@Test 注解的方法运行

*另外的一个注解 @Ignore,让某个方法不参与单元测试

*反射:得到Class类,有三种
*得到类里面的方法:getMethods得到所有的方法,得到某一个方法使用 getMethod
*得到方法,让方法去执行,使用 invoke(Object obj, Object... args)

6、练习二:使用自定义注解+反射模拟单元测试的效果
*自定义一个注解 @MyTest ,把这个注解写到某个方法上面,执行让这个方法去执行

*自定义注解,自定义注解默认只是在源代码阶段有效,设置使用范围,使用元注解
@Retention(RetentionPolicy.RUNTIME)
RetentionPolicy.CLASS:编译器将把注解记录在 class 文件中. 当运行 Java 程序时, JVM 不会保留注解. 这是默认值
RetentionPolicy.RUNTIME:编译器将把注释记录在class 文件中. 当运行 Java 程序时, JVM 会保留注解. 程序可以通过反射获取该注释
RetentionPolicy.SOURCE:编译器直接丢弃这种策略的注释

=代码
@Retention(RetentionPolicy.RUNTIME)
public@interface MyTest {

}

=代码
/*
* 首先得到类里面的所有的方法
* ** 使用反射得到类里面的所有的方法
* 其次得到有注解的方法
* 第三让带注解的方法执行
* */
publicstatic void main(String[] args) {
//让TestJunit02类里面带注解MyTest的方法执行
//得到Class类
Classclazz = TestJunit02.class;
//得到TestJunit02类里面的所有的方法
Method[]methods = clazz.getMethods();
//遍历数组
for(Method method : methods) {
//判断类里面的方法是否有注解
boolean flag =method.isAnnotationPresent(MyTest.class);
/*
* 自己定义的注解,这个注解有使用范围,在默认的情况下,范围只是在源代码阶段有效
* 在编译之后class里面还有注解,使用 元注解,设置使用范围
* */
// System.out.println(flag);
//得到方法里面带注解的方法
if(flag){
//让方法执行
try{
method.invoke(clazz.newInstance());
}catch (Exception e) {
e.printStackTrace();
}
}

}
}

7、练习三:使用注解完成数据库的查询的操作
*把数据库的一些连接的信息定义到注解的属性里面,在方法里面得到属性的内容,完成数据库的操作

*使用传统方式实现查询数据库的操作

*掌握如何获取自定义注解上面的属性值

=代码
//使用反射获取自定义注解里面的属性的值
Classclazz = TestJDBC02.class;
//得到注解所在的方法
Methodm1 = clazz.getMethod("selectUser");
//得到方法上面的注解
JdbcInfoinfo = m1.getAnnotation(JdbcInfo.class);
//得到注解里面的属性
Stringdrivername = info.drivername();
Stringurl = info.url();
Stringusername = info.username();
Stringpassword = info.password();

=============================================================
今天内容的总结
(1)文件上传
*掌握代码实现文件上传的操作
**上传三个要求
**代码实现的步骤

*掌握核心api里面的方法

*掌握解决相同的文件名的问题

(2)文件下载
*掌握文件下载的两种方式

*重点掌握代码实现文件的下载
**设置mime类型
**设置头 Content-Disposition

*掌握如果下载的文件名称包含中文解决方法
**使用User-Agent判断不同的浏览器

(3)注解
*掌握jdk里面的三个注解

*掌握自定义注解
**自定义注解里面属性类型只能是基本数据类型,string,Class,枚举,注解,一维数组

*掌握练习:使用注解+反射模式单元测试效果
**自定义注解的使用范围:源代码阶段,使用元注解设置 @Retention(RetentionPolicy.RUNTIME)

*掌握如何获取注解上面的属性值(反射获取)

*把文件上传和下载的代码至少写两遍
*使用注解+反射模式单元测试效果代码至少写一遍

</pre>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  黑马课程