对于JavaWeb中的上传与下载,别说你从没遇到过。
当我们要向网站中上传文件或者需要从网站上下载文件时,其实我们就已经玩了一遍上传与下载了。那么我们上传的文件会保存在服务器的哪里呢?下载的文件又是从服务器的哪里传过来的?这些又是怎么实现的呢?
另外很多时候我们注册某个公司的账号时,注册成功后该公司会给你填入的邮箱发送一封邮件,要求你点击邮件中的链接来给该账号激活。这个功能又是怎么实现的呢?
下面我将告诉你如何用代码实现这些功能。
1.对于多部件表单的体的理解
通过抓包的方式可以看到服务器发送过来的响应,理解如下:
- 1.分隔出多个部件,即一个表单项一个部件。
- 2.一个部件中包含请求头和空行,以及请求体。
- 3.普通表单项:
1个头:Context-Disposition:包含name=“xxxx”,即表单项名称。
体就是表单项的值。 - 4.文件表单项:
有2个头:a.Content-Disposition:包含name=“xxxx”,即表单项名称;还有一个filename=“xxx”,表示上传文件的名称。
Content-Type:它是上传文件的MIME类型,例如:image/pjpeg,表示上传的是图片,图片为jpg扩展名的图片。
体就是上传文件的内容。
2.实现文件的上传
首先我们需要明确的是要实现上传和下载功能都要借助第三方Jar包 fileupload.jar
,依赖jar包为:commons-io.jar
。
2.1上传对表单的限制
- 1.method必须为”post”.
- 2.必须对表单form添加
enctype="multipart/form-data"
的属性。 - 3.表单中需要添加文件表单项:
<input type="file" name="xxx"/>
,除了文件表单项叫文件表单项外其他所有的input表单项都叫普通表单项。
2.2上传对servlet的限制
- 1.在servlet中通过方法
request.getParameter("xxx");
得到表单中的参数时得到返回值不再是Object对象,而是返回的null,因为文件表单form添加了enctype="multipart/form-data
的属性。 - 2.解决方法:在Servlet中通过方法
ServletInputStream request.getInputStream();
得到表单中的参数,然而得到的内容将是表单的整个请求体。
2.3上传的代码实现
涉及到的类:
- 1.工厂类:FileItemFactory类
- 2.解析器类:ServletFileUpload类
- 3.表单项:FileItem
代码步骤:
- 1.创建工厂:
DiskFileItemFactory factory=new DiskFileItemFactory();
- 2.创建解析器:
ServletFileUpload sfu=new ServletFileUpload(factory);
- 3.使用解析器来解析request,得到的是FileItem集合:
List<FileItem> fileItemList=sfu.parseRequest(request);
Demo:
FileItem的API
boolean isFormField()
:是否为普通表单项,如果返回true则为普通表单项,如果为false则为文件表单项。String getFieldName()
:返回当前表单项的名称。String getString(String charset)
:返回表单项的值,参数传入utf-8即可。这个方法不适合文件表单项。String getName()
:返回上传的文件名称。long getSize()
:返回上传文件的字节数。InputStream getInputStream()
:返回上传文件对应的输入流。void write(File file)
:把上传的文件内容保存到指定的文件中。String getContextType()
;
2.4上传的细节
- 1.文件必须保存到WEB-INF文件下
为了不让浏览器直接访问到。
- 2.文件名称相关问题
- a.有的浏览器上传的文件名是绝对路径,如
c:\file\图片.jpg
,这时需要对文件名进行切割,切割的代码为:String fileName=file2.getName();//file2为FileItem对象 int index=fileName.lastIndex("\\"); if(index!=-1) { fileName=fileName.substring(index+1); }
- b.文件名乱码或者普通表单项乱码
解决方法:告诉fileupload你的编码方式,在servlet中设置代码
request.setCharacterEncoding("utf-8");
因为fileupload内部会调用request.getCharacterEncoding();
得到你的编码方式。
解决方法1,在servlet中添加:request.setCharacterEncoding("utf-8");//优先级比下面的方式低
解决方法2,在servlet中添加:servletFileUpload.setHeaderEncoding("utf-8");//优先级比上面的方式低。
也就是说,当同时出现解决方法1中的代码和解决方法2中的代码时,会优先使用方法2来设置编码方式。 - c.文件同名问题:我们需要为上传过来的每个文件都添加名称前缀,而且这个前缀要保证不能重复(使用uuid解决).
fileName=CommonUtils.uuid()+”_”+fileName;
- a.有的浏览器上传的文件名是绝对路径,如
- 3.因为不能在一个目录下存放过多文件,所以我们需要对目录进行打散,打散的方法有如下3种:
- a.首字符打散:使用文件的首字母作为目录名称,例如:abc.text,那么我们就把文件保存到a目录下,如果a目录不存在,那么创建之。(缺点:若文件名称为中文咋办?)
- b.时间打散:使用当前日期作为目录
- 哈希打散:(缺点,存在目录下的文件我们不清楚)1.通过文件名称得到int值,即调用hashCode();2.把int值转换成16进制0-9,A-F;3.获取16进制的前2位用来生成目录,目录为二层!例如:1B2C3D4E5F,生成的目录为/1/B,在其下保存文件。
- 4.上传文件的大小限制
- a.单个文件的大小限制
1.
sfu.setFilesizeMax(100*1024);
限制单个文件大小为100kb,此方法调用必须在解析开始之前调用。
2.如果上传的文件超过限制,则在parseRequest()
方法时,会抛出异常。 - b.整个请求所有文件大小限制
sfu.setSizeMax(1024*1024);
限制整个表单大小为1M。此方法调用必须在解析开始之前调用。
如果上传的文件超过限制,则在parseRequest()方法时,会抛出异常。超出限制大小时显示错误信息代码:需要在jsp文件中增加${msg}。
代码如下:
- a.单个文件的大小限制
- 5.缓存大小与临时目录
- 缓存大小:超出多大,才向硬盘中保存。默认为10KB。
- 临时目录:向硬盘的什么目录保存。
说明:设置缓存大小与临时目录,在创建工厂时,调用DiskFileItemFactory的有参构造器:DiskFileItemFactory factory=new DiskFileItemFactory(缓存大小,硬盘临时目录路径);。当文件正在上传时,会在硬盘的该临时目录下出现这个文件;而当文件上传完毕时,硬盘下该临时目录下的这个文件就会消失。最后保存在web-inf的target目录下。
3.实现文件的下载
下文文件就是向客户端响应字节数据。当不涉及文件下载时你向服务器发出一个请求时,服务器返回的响应都是html的字符数据;而当涉及到文件下载时,服务器会把一个文件变成字节数组,使用response.getOutputStream();
来响应给浏览器。
3.1文件下载的要求
设置两个头一个流:
- Content-Type:你传递给客户端的文件是什么MIME类型,例如:image/pjpeg。可以通过文件名称调用ServletContext的getMimeType()方法得到MIME类型。
- Content-Disposition:它的默认值为inline,表示在浏览器窗口中打开。
attachment;fileName=xxx;
在fileName后面跟随的是显示在下载框中的文件名称。 - 流:要下载的文件数据。自己new一个输入流即可。
3.2下载Demo
3.3下载的细节
1.显示在下载框中的中文名称时,会出现乱码。
- FireFox:Base64编码
- 其它大部分浏览器:url编码
通用方案:frameName=new String(filename.getBytes("GBK"),"ISO-8859-1");
代码见下:
注意downutil.jar包的使用
4.JavaMail
JavaMail是Java提供的一组API,用来发送和接收邮件。
需要导入的jar包为:1.mail.jar2.activation.jar
4.1与邮件相关的协议
smtp 25–>简单的邮件传输协议。
pop3 110–>邮局协议第三版
4.2核心类
1.Session(这跟我们Servlet中的HttpSession不同哦).
如果你得到了它,表示已经与服务器连接上了,与Connectin的作用相似。
得到Session,需要使用Session.getInstance(Properties,authenticator);
方法。
得到Session对象的Demo如下:
Properties props=new Properties();
props.setProperty(“mail.host”,”smtp.163.com”);
props.setProperty(“mail.smtp.auth”,”true”);
Authenticator auth=new Authenticator(){
protected PasswordAuthentication getPasswordAuthentication(){
return new PasswordAuthentication(“username”,”password”);
}
};
Session session=Session.getInstance(props,auth);
2.MimeMessage
它表示一个邮件对象,你可以调用它的setFrom()
方法进行设置发件人、设置主题、设置正文。
3.TransPort
它只有一个发邮件的功能。
4.3完整Demo如下
4.4带有附件的邮件
2018.3.19更
欢迎加入我的Java交流1群:659957958。
2018.4.21更:如果群1已满或者无法加入,请加Java学习交流2群:305335626 。
5.联系
If you have some questions after you see this article,you can tell your doubts in the comments area or you can find some info by clicking these links.