<pre>

 

day12

 

上节内容回顾

         1jstl的标签

                   *if  choose 

                   *forEach

                   <c:forEachvar="l" items="${list}">

 

         2jsp开发模式

                   *模型一

                   *模型二(mvc模式)

                   **mvc模式

                   ***m:模型,使用javabean

                   ***v:视图,使用jsp

                   ***c:控制器,使用servlet

 

                   *dao模式:数据访问对象,专注于对数据库的操作

                   **首先创建接口,在接口里面定义操作数据库的方法

                   **其次,创建类实现这个接口里面的方法

 

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

 

         3、使用mvc+dao模式案例

 思维导图

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

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-Agentrequest.getHeader("User-Agent");

         *不同的浏览器需要不同的编码,ie采用url编码,火狐采用base64编码

         *判断不同的浏览器,不同的浏览器采用不同的处理方式

 

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

         上午内容的总结

         1)掌握如何使用代码实现文件的上传

                   *上传的三个要求

                   *上传步骤

                   *文件重名问题的解决方法

 

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

 

 

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

 

         3)如果要下载的文件名称包含中文,会乱码问题

         *如果想要得到当前请求的浏览器:使用头信息 User-Agentrequest.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) {}

                   可变参数理解为数组

        

         *注解就是代码里面特殊的标记

         *语法 @注解的名称

        

         2jdk里面的注解有三个注解

         第一个注解:@Override,限定重写父类的方法名称

                   jdk1.6开始,支持接口和实现类的 使用 @Override

        

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

 

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

 

         3)自定义注解

         *定义方式: @interface 自定义注解的名称 {

                   注解上面的属性: 类型 名称();

         }

         *注解上面的属性的类型:基本数据类型、stringClass、注解、枚举以及这些的一维数组

 

         *在方法上面使用:

         =代码

         @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里面的三个注解

                  

                   *掌握自定义注解

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

                  

                   *掌握练习:使用注解+反射模式单元测试效果

                            **自定义注解的使用范围:源代码阶段,使用元注解设置 @Retention(RetentionPolicy.RUNTIME)

                  

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

        

         *把文件上传和下载的代码至少写两遍

         *使用注解+反射模式单元测试效果代码至少写一遍


</pre>