JavaWeb之上传下载与JavaMail

title: JavaWeb之上传下载与JavaMail
tag: JavaWeb upload JavaMail
categories: JavaWeb upload JavaMail


若图片无法显示,请前往我的博客查看,相应文章链接:http://codingxiaxw.cn/2016/11/02/31-%E4%B8%8A%E4%BC%A0%E4%B8%8B%E8%BD%BD%E4%B8%8EJavaMail/

对于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:

image

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;

  • 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}。
代码如下: [图片上传失败...(image-e3398e-1526285981316)]

  • 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

[图片上传失败...(image-6e30dd-1526285981316)]

3.3下载的细节

1.显示在下载框中的中文名称时,会出现乱码。

  • FireFox:Base64编码
  • 其它大部分浏览器:url编码

通用方案:frameName=new String(filename.getBytes("GBK"),"ISO-8859-1");

代码见下:[图片上传失败...(image-2fb7a5-1526285981316)]

注意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如下

[图片上传失败...(image-1bdb39-1526285981316)]

4.4带有附件的邮件

image

2018.3.19更

欢迎加入我的Java交流1群:659957958。群里目前已有1800人,每天都非常活跃,但为了筛选掉那些不怀好意的朋友进来搞破坏,所以目前入群方式已改成了付费方式,你只需要支付9块钱,即可获取到群文件中的所有干货以及群里面各位前辈们的疑惑解答;为了鼓励良好风气的发展,让每个新人提出的问题都得到解决,所以我将得到的入群收费收入都以红包的形式发放到那些主动给新手们解决疑惑的朋友手中。在这里,我们除了谈技术,还谈生活、谈理想;在这里,我们为你的学习方向指明方向,为你以后的求职道路提供指路明灯;在这里,我们把所有好用的干货都与你分享。还在等什么,快加入我们吧!

2018.4.21更:如果群1已满或者无法加入,请加Java学习交流2群:305335626 。群2作为群1的附属群,除了日常的技术交流、资料分享、学习方向指明外,还会在每年互联网的秋春招时节在群内发布大量的互联网内推方式,话不多说,快上车吧!

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.

推荐阅读更多精彩内容