分享

javaee之文件上传和下载

 Dragon_chen 2017-05-15
件上传
为什么使用文件上传?
例如社交网站需要提供用户照片.
很多企业内部,商品网站维护时需要上传一些文件
邮箱需要上传附件....
怎样完成文件上传操作?
浏览器
1.请求方式必须是 post
2.需要使用组件<input type="file" name="f">
3.表单必须设置encType="multipart/form-data"
4.上传文件的input 中,name属性必须要填写,不然不会上传. 
服务器端
通过request对象,获取InputStream,可以将浏览器提交的所有请求正文读取到.
-------------------------------------------------------------------------------------
文件上传的插件
commons-fileupload 它是常用的一个文件上传工具
使用时要导入两个jar包
commons-fileupload-1.2.1.jar
commons-io-1.4.jar
快速入门
1.设置浏览器端
<form action="${pageContext.request.contextPath}/upload2" method="post" enctype="multipart/form-data">
<input type="file" name="f"> 
<input type="submit" value="上传">
</form>
2.在服务器端操作
// 1.创建一个 DiskFileItemFactory
DiskFileItemFactory factory = new DiskFileItemFactory();

// 2.创建一个ServletFileUpload
ServletFileUpload upload = new ServletFileUpload(factory);

// 3.完成上传操作

try {
// 3.1 得到所有上传项
List<FileItem> items = upload.parseRequest(request);

// 3.2遍历上传项
for (FileItem item : items) {

// 3.3判断是否是上传组件
if (!item.isFormField()) {

// 3.4 将文件真正上传
IOUtils.copy(item.getInputStream(), new FileOutputStream(
"d:/upload/a.txt"));
}

}

} catch (FileUploadException e) {
e.printStackTrace();
}
=============================================================
文件上传 后台步骤
1. 创建并使用DiskFileItemFactory 进行上传的设置(缓冲区大小,临时文件仓库)
2. 创建ServletFileUpload类,加载DiskFileItemFactory中的配置.
3. 使用ServletFileUpload 解析Request对象. =>  获得请求正文中每个分隔符之间的内容 封装的FileItem对象 集合.
4. 遍历FileItem对象集合.
//判断 当前FIleItem是否是一个字段类型
//不是字段类型 => 该FileItem 是 上传文件的流类型 => 对文件进行保存
//是字段类型 => 获得字段内容.

=============================================================
三个核心类
1.DiskFileItemFactory
用于设置文件上传时,缓冲区大小以及临时文件存储位置.
//1.1 可以设置文件上传缓冲区大小
factory.setSizeThreshold(1024*1024); //设置为1m 默认是10k
//1.2可以设置临时文件存储位置
File temp=new File(this.getServletContext().getRealPath("/temp"));
factory.setRepository(temp); //可以指定临时文件存储位置,默认是系统的临时文件存储位置
2.ServletFileUpload
它的作用是真正完成解析数据操作上传类
1.pareRequest(HttpServletRequest request)
得到所有的上传信息,将每一部分映射成FileItem对象.
2.isMultipartContent(HttpServletRequest request)
这个方法返回值是boolean,它是用于判断当前表单是否是一个上传的表单,
简单说,就判断它的encType的值是否是 multipart/form-data.
3.setHeaderEncoding(String encoding)
用于解决上传文件名称中文乱码问题
4.可以设置上传文件大小,如果超过设置大小.将会抛出异常(默认没有最大值)
void setFileSizeMax(long fileSizeMax) 设置单个文件上传大小 
void  setSizeMax(long sizeMax) 设置总文件上传大小 

3.FileItem
它代表的是每一个上传项(其时就是表单中的每一个组件)
1.isFormField()
这个方法返回的是boolean类型,
它是判断当前组件是否是上传组件 简单说,就是判断type="file",
如果返回true,代表不是上传组件,返回false,代表是上传组件
2.getName()
获取非上传组件的上传文件的名称,如果是非上传组件,返回的是null
3.getFieldName()
获取组件名称,提交的键
4.getString()
获取非上传组件的value值,
通过它也可以获取上传的文件内容,但是,使用它获取不合适。
如果使用了commons-fileupload进行文件上传,而上传表单中包含了
非上传组件,获取其值,不能使用request获取.
getString()有一个重载的方法 getString(String encoding)可以解决乱码问题
问题:使用getString()去获取上传文件内容不合适,那么怎样操作?
基本操作:
通过FileItem.getInputStream();可以获取一个输入流,这个输入流就可以
读取出上传文件内容。
InputStream is = item.getInputStream(); // 用于读取上传文件内容的输入流.
FileOutputStream fos = new FileOutputStream(
"d:/upload/" + filename);

int len = -1;
byte[] b = new byte[1024 * 10];

while ((len = is.read(b)) != -1) {
fos.write(b, 0, len);
fos.flush();
}
fos.close();
is.close();

可以使用IOUtils工具完成文件copy操作
IOUtils.copy(is, fos);
5.delete方法
它是用于上传完成后,删除临时文件的
-----------------------------------------------
问题:关于文件上传时文件名称包含中文的乱码问题?
调用ServletFileUpload的setHeaderEncoding方法
-----------------------------------------------
问题:普通表单字段提交时,中文乱码问题?
FileItem的 getString方法返回提交的值.通过参数来设置解码码表
例如: getString("UTF-8");
-----------------------------------------------
问题: 如何设置上传文件大小限制?
调用ServletFileUpload的setSizeMax(总大小)或setFileSizeMax(每个大小)方法
------------------------------------------------
问题:上传文件后,我们应该把文件保存到什么位置?
1.上传后需要其他用户可以直接访问,照片
放到webRoot下.
2.上传后其他用户不能直接访问,CSDN
放到webRoot以外的文件夹. WEB-INF下或硬盘其他位置 .例如 d:/xxx
-----------------------------------------------------------------------------------
问题: 上传文件时,该使用什么名字将文件保存?
文件名称不能使用用户命名的名称.
用户命名的名称我们需要保存到数据库中,在下载时使用.
不是用用户命名的名称,我们以什么名称保存文件(不能重复)?
1.UUID
2.登录用户名+当前系统毫秒数
如何获得用户命名的文件名?
getName方法即可获得文件名.
IE浏览器中,会把文件的在客户端的完整路径传过来.需要截取.
其他浏览器只会文件名称传过来.直接使用,无需截取.
-----------------------------------------------------------------------------------
问题:为了防止同一个目录下方上传文件数量过多,思考如何解决? 
1> /upload/2015/07/22/xxxx  使用当前日期作为子文件夹名称
2> 当前登录用户的用户名作为文件夹名称
============================================================================
多文件上传
原理: 在form中添加多个type="file"的input即可.
实现: 使用JS实现,动态添加多个type="file"的input.
===================================================================================================
3.文件下载
文件下载方式
1. 直接将待下载文件放到webRoot文件夹或子文件夹下.
2.需要控制资源的下载.在下载前要经过逻辑处理.
将资源放到用户不能直接访问的地方. 例如: WEB-INF  或  d:/upload
------------------------------------------------------------------------------------
下载编码问题:
1. 如何使用一个servlet 提供多个文件的下载
  使用参数传递下载的文件名称
2.客户端的超连接中如果包含了中文,会出现get请求方式乱码问题
<a href="${pageContext.request.contextPath}/download?filename=天空.mp3">下载王非天空.mp3</a>
String filename = request.getParameter("name"); // yog.rar / 她说.mp3
byte[] b = filename.getBytes("ISO-8859-1");
filename = new String(b,"UTF-8");
实际上Get提交时包含中文乱码的解决。
3.关于下载文件名称中文乱码问题
response.setHeader("content-disposition", "attachment;filename="+ filename);
这段代码中的filename是指定下载文件时的名称
-------------------------------------------------------------------------------------------------------------------
注解 annotation
什么是注解?它有什么作用?
注解就是  @xxx  这样的东西就是注解.
注释:给程序员看的.
注解:给程序看。
使用注解的目的: 其实将来使用注解目的就是为了代替传统配置文件.
------------------------------------------------
jdk中提供的三个注解
1.@Override
它是用来描述当前方法是一个重写的方法.
作用:在编译阶段对方法进行检查
注意事项:
jdk1.5: 它只能描述继承中的重写
jdk1.6: 它可以描述接口实现的重写,也能描述类的继承的重写
2.@Deprecated
它是用于描述当前方法是一个过时的方法.
问题:为什么描述这个方法过时了?
在旧的版本中的方法在新的版本中已经有了更好的实现,
旧的版本中的方法存在安全隐患,不建议使用时,这样的方法就可以标注成过时方法。
3.@SuppressWarnings("all")
对程序中的警告去除。
@SuppressWarnings("all")去除所有警告.
@SuppressWarnings("unused")去除未被使用的变量的警告.
@SuppressWarnings("null")可能出现空指针的警告.
----------------------------------------------------------
自定义注解
通过上面的三个注解的源代码可以发现,要声明一个注解通过  @interface
声明一个注解格式  @interface 注解名{}
分析一下注解的本质:
将其.class文件找到,反编译.
@interface MyAnnoation{}
反编译后的结果
interface MyAnnotation extends Annotation
{
}
  结论:注解本质上就是一个接口。它扩展了java.lang.annotation.Annotation接口;
  
  在java中所有注解都是Annotation接口的子接口。
  
 注解也是jdk1.5的一个新特性.

----------------------------------------
注解的成员
注解本质上就是一个接口,那么它也可以有属性和方法。
但是接口中的属性是  static final的,在注解中注解没有什么意义。
在开发中注解中经常存在的是方法。而在注解中叫做注解的属性.
  
注解的属性的类型:
1.基本类型
2.String
3.枚举类型
4.注解类型
5.Class类型
6.以上类型的一维数组类型
关于注解的属性声明后的使用:
1.如果一个注解有属性,那么在使用注解时,要对属性进行赋值操作.
例如:@MyAnnotation3(st = "aaa")
2.如果一个注解的属性有多个,都需要赋值,使用","分开属性.
@MyAnnotation3(st = "aaa",i=10)
3.也可以给属性赋默认值
double d() default 1.23;
如果属性有默认值,在使用注解时,就可以不用为属性赋值。
4.如果属性是数组类型
1.可以直接使用   属性名={值1,值2,。。。}方式,例如
@MyAnnotation3(st = "aaa",i=10,sts={"a","b"})
2.如果数组的值只有一个也可以写成下面方式
@MyAnnotation3(st = "aaa",i=10,sts="a")
注意sts属性它是数组类型,也就是说,只有一个值时,可以省略"{}"
5.对于属性名称 value的操作.
1.如果属性名称叫value,那么在使用时,可以省略属性名称
@MyAnnotation3("hello")
2.如果有多个属性,都需要赋值,其中一个叫value,这时,必须写属性名称
@MyAnnotation3(value="hello",i=10)
3.如果属性名称叫value,它的类型是数组类型.
1.只有这个value属性
可以直接赋值,不能写属性名称,但是,如果只有一个值
@MyAnnotation3({"abc"})或  @MyAnnotation3("abc")
但是如果有多个值
@MyAnnotation3({"abc","def"})
2.如果有多个属性,属性名称叫value
所有属性都需要赋值,那么必须写属性名称.
--------------------------------------------------------------------------------------
1、元注解

元注解是指注解的注解。包括  @Retention @Target @Document @Inherited四种。


1.1、@Retention: 定义注解的保留策略

@Retention(RetentionPolicy.SOURCE)   //注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy.CLASS)     // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
@Retention(RetentionPolicy.RUNTIME)  // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
 
1.2、@Target:定义注解的作用目标
@Target(ElementType.TYPE)   //接口、类、枚举、注解
@Target(ElementType.FIELD) //字段、枚举的常量
@Target(ElementType.METHOD) //方法
@Target(ElementType.PARAMETER) //方法参数
@Target(ElementType.CONSTRUCTOR)  //构造函数
@Target(ElementType.LOCAL_VARIABLE)//局部变量
@Target(ElementType.ANNOTATION_TYPE)//注解
@Target(ElementType.PACKAGE) ///包   
由以上的源码可以知道,他的elementType 可以有多个,一个注解可以为类的,方法的,字段的等等
1.3、@Document:说明该注解将被包含在javadoc中
 
1.4、@Inherited:说明子类可以继承父类中的该注解
----------------------------------------------------------------------------------
自定义注解要有功能,需要结合反射去完成操作.
可以使用注解来替换配置文件.
案例:银行最大转账金额.
注解使用---让注解具有功能,需要结合反射来完成.
1.创建一个注解  BankInfo,它具有一个属性 maxMoney类型是double类型.
2.添加两个元注解来描述注解是在runtime阶段有效果,并且是应用在方法上的.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface BankInfo {

double maxMoney();
}
3.在银行转账的方法上使用这个注解
@BankInfo(maxMoney=30000)
public void acccount(String name1, String name2, double money) 
4.在account方法中,获取注解上的maxMoney属性值.
要结合反射来完成
1.在这个方法中获取本方法的Method对象
2.通过Method类中的getAnnotation(Class c)
参数就是注解类型的Class对象

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多