JavaWeb了解之入门篇4(发邮件、文件上传和下载)

目录
    1. 发邮件
    2. 上传
    3. 下载

本篇笔记是从个人笔记中摘录,不属于教程

1. 发邮件

很多网站在用户注册之后,会给用户发送一封来自注册网站的邮件(通常包含一个用来激活账号的超链接),或者在修改密码时会向用户绑定的邮箱发送邮件(包含一个超链接,点击后会通过验证)进行验证。

  1. 概念
邮件服务器
  要想在Internet上提供电子邮件功能,则需要邮件服务器(新浪邮箱、QQ邮箱等都有自己的邮件服务器)。
  发件人和收件人首先需要在邮件服务器上申请电子邮箱帐号,申请成功后 邮件服务器会为该邮箱账号分配一定的空间,发件人向收件人发送邮件时,邮件会发送到邮件服务器上收件人邮箱账号分配的空间中。
  邮件服务器包含了一个SMTP服务器和一个POP3服务器。

邮件传输协议(协议中定义了通讯规则)
  发送邮件,需遵循SMTP协议。
    处理用户smtp请求(邮件发送请求)的服务器称之为SMTP服务器(邮件发送服务器)。
  接收邮件,需遵循POP3协议。
    处理用户pop3请求(邮件接收请求)的服务器称之为POP3服务器(邮件接收服务器)。

不同邮箱服务器之间的发件、收件步骤(如QQ邮箱向新浪邮箱发送邮件)
  发件人点击发送邮件按钮
    1. 通过smtp协议连接并发送邮件到发件邮箱所属的邮件服务器1的smtp服务器上;
    2. 邮件服务器1的smtp服务器通过smtp协议将邮件发送到收件邮箱所属邮件服务器2的smtp服务器上,并存储在收件箱账号所拥有的存储空间中。
  收件人
    1. 通过pop3协议连接邮件服务器2的POP3服务器;
    2. POP3服务器从收件箱账号所拥有的存储空间中查阅邮件显示给收件人。

SMTP协议(说明)
  使用smtp协议发送邮件给邮件服务器时,规定了要做以下几件事:
  1.(使用ehlo命令)和连接上的smtp服务器打声招呼
    ehlo hello
  2. 登陆smtp服务器(登录使用的用户名密码必须经过Base64加密)
    auth login
    Base64加密后的用户名
    Base64加密后的密码
  3. 指明邮件的发件人和收件人
    MAIL FROM:发件邮箱
    RCPT TO:收件邮箱
  4. 编写邮件内容(邮件的编写格式有一定的规则)邮件头+邮件主体内容
    DATA
    From: 发件邮箱
    To: 收件邮箱
    Subject: 邮件的主题
    空格
    邮件内容
  5. 输入一个.表示邮件内容完毕
    . 
  6. 断开与邮件服务器的连接
    QUIT 
/*
  例:在Windows系统终端下可使用Telnet(Window自带的网络客户端程序,通过它可以连接上互联网上面的任意一台主机)来向搜狐邮箱发送邮件。
  在终端输入:telnet smtp.sohu.com 25    表示连接搜狐的smtp服务器 端口25
  然后依次输出上面的6步骤。
  打开搜狐邮箱,会发现已经收到邮件。
*/

POP3协议(说明)
  使用POP3协议收取邮件时,规定了要做以下几件事:
  1. 输入用户名和密码登录到POP3服务器(无需Base64加密)
    user username --登录邮箱的用户名
    pass password --登录邮箱的密码
  2. 使用retr命令收取邮件
    retr 数字  --读取第一封邮件则retr 1(内容经过Base64加密)
    查看邮箱里面的邮件数量以及邮件占用的空间大小信息: stat
    获取某一封邮件的统计信息:list 数字
  3. 邮件收取后使用quit命令断开与POP3服务器的连接。
    quit 
/*
例:在Windows系统终端下可使用Telnet收取邮件。
在终端输入:telnet pop3.sohu.com 110 表示连接搜狐的pop3服务器 端口110
输入:user 用户名
输入:pass 密码
输入:start  查看邮箱里面的邮件数量以及邮件占用的空间大小信息
输入:retr 1 查看第一封邮件(内容是经过Base64加密的)
输入:.  断开连接
*/

Base64加密(对用户名密码进行Base64加密)
        System.out.print("请输入用户名:");
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        String userName = in.readLine();
        System.out.print("请输入密码:");
        String password = in.readLine();
        BASE64Encoder encoder = new BASE64Encoder();
        System.out.println("编码后的用户名为:" + encoder.encode(userName.getBytes()));
        System.out.println("编码后的密码为:" + encoder.encode(password.getBytes()));
        
Base64解密(对邮件内容进行Base64解密)
        BASE64Decoder decoder = new BASE64Decoder();
        // 邮件主题的Base64编码
        String emailSubject = "=?GBK?B?08q8/rLiytQ=?=";
        // 邮件文本内容的Base64编码
        String emailPlainContent = "vPK1pbXE08q8/reiy82y4srUo6E=";
        // 带html标签和邮件内容的Base64编码
        String emailHtmlContent = "PFA+vPK1pbXE08q8/reiy82y4srUo6E8L1A+";
        // 将使用Base64编码过后的文本内容再使用Base64来解码
        emailSubject = new String(decoder.decodeBuffer(emailSubject),"GBK");
        emailPlainContent = new String(decoder.decodeBuffer(emailPlainContent),"GBK");
        emailHtmlContent = new String(decoder.decodeBuffer(emailHtmlContent),"GBK");
        System.out.println("邮件标题:"+emailSubject);
        System.out.println("邮件内容:"+emailPlainContent);
        System.out.println("带html标签的邮件内容:"+emailHtmlContent);
RFC882文档协议(不能发送图片、附件)
  规定了如何编写一封简单的邮件(纯文本邮件),包含邮件头和邮件体两部分(使用空行分隔)。
  邮件头包含:
    from字段     --指明发件人
    to字段       --指明收件人
    subject字段   --指明邮件主题
    cc字段       --抄送,将邮件发送给收件人的同时抄送给另一个收件人,收件人可以看到邮件抄送给了谁
    bcc字段      --密送,将邮件发送给收件人的同时将邮件秘密发送给另一个收件人,收件人无法看到邮件密送给了谁
  邮件体:
    具体内容

MIME协议(对RFC822文档的升级和补充,可包含图片、附件)
  MIME协议描述的邮件称之为MIME邮件。
  MIME协议描述的数据称之为MIME消息。
  对于一封复杂邮件,如果包含了多个不同的数据,MIME协议规定了要使用分隔线对多段数据进行分隔,并使用Content-Type头字段对数据的类型、以及多个数据之间的关系进行描述。

JavaMail创建邮件(基于MIME协议)和发送/接收邮件
  JavaMail API:
    1. Message类:创建邮件、解析邮件内容。实例对象代表 一封邮件。
    2. Transport类:发送邮件。实例对象代表 实现了某邮件发送协议(SMTP协议)的邮件发送对象。
    3. Store类:接收邮件。实例对象代表 实现了某邮件接收协议(POP3协议)的邮件接收对象。
    4. Session类:客户端与邮件服务器建立连接的会话信息(邮件服务器的主机名、端口号、采用的邮件发送和接收协议等),session对象根据这些信息构建用于邮件收发的Stroe和Transport对象,以及为客户端创建Message对象时提供信息。
发送邮件的步骤:
    1. 创建session对象
    2. 创建message对象
    3. 创建transport对象、连接服务器、发送邮件、关闭连接。
/*
MimeMessage对象表示整封邮件,包含MIME对象的消息头和MimeMultipart对象。
MimeMultipart对象表示一个由多个MIME消息组合成的MIME消息。
MimeBodyPart对象表示邮件的一个MIME消息,MimeBodyPart对象中可设置MimeMultipart对象。
*/
  1. 简单使用
  1. 下载并导入所需的jar包
    在Oracle官网下载所需的mail.jar包activation.jar包,放在WEB-INF的lib目录下

  2. 相关文件
    注册页

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    <title>My JSP 'MyJsp.jsp' starting page</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">
    <!--
    <link rel="stylesheet" type="text/css" href="styles.css">
    -->
  </head>
  <body>
    <form action="${pageContext.request.contextPath}/servlet/RegisterServlet" method="post">
        用户名:<input type="text" name="username"><br/>
        密码:<input type="password" name="password"><br/>
        邮箱:<input type="text" name="email"><br/>
        <input type="submit" value="注册">
    </form>
  </body>
</html>

注册成功后的显示页面

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    <title>My JSP 'message.jsp' starting page</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">
    <!--
    <link rel="stylesheet" type="text/css" href="styles.css">
    -->
  </head>
  <body>
    ${message}
  </body>
</html>

处理表单请求

package com.sst.cx;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/RegisterServlet")
public class RegisterServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    public RegisterServlet() {
        super();
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      try{
            String username = request.getParameter("username");
            String password = request.getParameter("password");
            String email = request.getParameter("email");
            User user = new User();
            user.setEmail(email);
            user.setPassword(password);
            user.setUsername(username);
            System.out.println("把用户信息注册到数据库中");
            // 用户注册成功之后就使用用户注册时的邮箱给用户发送一封Email
            // 发送邮件非常耗时,创建另一个线程来专门发送邮件
            SendMail send = new SendMail(user);
            // 启动线程,线程启动之后就会执行run方法来发送邮件
            send.start();
            // 注册用户
            // new UserService().registerUser(user);
            request.setAttribute("message", "恭喜您,注册成功,我们已经发了一封带了注册信息的电子邮件,请查收,如果没有收到,可能是网络原因,过一会儿就收到了!!");
            request.getRequestDispatcher("/message.jsp").forward(request, response);
      }catch (Exception e) {
            e.printStackTrace();
            request.setAttribute("message", "注册失败!!");
            request.getRequestDispatcher("/message.jsp").forward(request, response);
      }
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }
}

用户类

package com.sst.cx;
public class User {
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    private String username;
    private String password;
    private String email;
}

发送邮件类

package com.sst.cx;
import java.util.Properties;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
public class SendMail extends Thread {
    // 发件邮箱
    private String from = "chenxiang@sohu.com";
    // 发件邮箱的用户名
    private String username = "chenxiang";
    // 发件邮箱的密码
    private String password = "密码";
    // 发送邮件的服务器
    private String host = "smtp.sohu.com";
    // 用户模型
    private User user;
    public SendMail(User user){
        this.user = user;
    }
    /* 重写run方法的实现,在run方法中发送邮件给指定的用户
     * @see java.lang.Thread#run()
     */
    @Override
    public void run() {
        try{
            // 1. 创建Session
            // 参数
            Properties prop = new Properties();
            // 设置 服务器
            prop.setProperty("mail.host", host);
            // 设置 协议
            prop.setProperty("mail.transport.protocol", "smtp");
            // 设置 是否认证
            prop.setProperty("mail.smtp.auth", "true");
            // 设置 端口
            prop.setProperty("mail.smtp.port", "25");
            // 设置 超时时间
            prop.setProperty("mail.smtp.timeout","1000");
            // 设置 开启tls
            prop.put("mail.smtp.starttls.enable", "true");
            // 
            Session session = Session.getInstance(prop);
            // 开启Session的debug模式,可查看到程序发送Email的运行状态。
            session.setDebug(true); 
            // 2. 创建邮件
            Message message = createEmail(session,user);
            // 3. 连接邮箱服务器并登录邮箱 ,发送邮件,关闭连接
            Transport ts = session.getTransport();
            ts.connect(host, username, password);
            ts.sendMessage(message, message.getAllRecipients());  // 通常会有延迟。
            ts.close();
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    /**
    创建要发送的邮件
    */ 
    public Message createEmail(Session session,User user) throws Exception{
        // 创建邮件
        MimeMessage message = new MimeMessage(session);
        // 设置 发件邮箱
        message.setFrom(new InternetAddress(from));
        // 设置 收件邮箱
        message.setRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));
        // 设置 邮件主题
        message.setSubject("用户注册邮件");
/* 
纯文本
*/
        // 设置 邮件内容
        String info = "恭喜您注册成功,您的用户名:" + user.getUsername() + ",您的密码:" + user.getPassword() + ",请妥善保管,如有问题请联系网站客服!!";
        message.setContent(info, "text/html;charset=UTF-8");
        // 保存
        message.saveChanges();
/*
带图片
        // 文本
        MimeBodyPart text = new MimeBodyPart();
        text.setContent("这是一封邮件正文带图片<img src=''>的邮件", "text/html;charset=UTF-8");
        // 图片
        MimeBodyPart image = new MimeBodyPart();
        DataHandler dh = new DataHandler(new FileDataSource("图片路径"));
        image.setDataHandler(dh);
        image.setContentID("xxx.jpg");
        // 描述数据关系
        MimeMultipart mm = new MimeMultipart();
        mm.addBodyPart(text);
        mm.addBodyPart(image);
        mm.setSubType("related");
        //
        message.setContent(mm);
        message.saveChanges();
        // 保存创建好的邮件到本地
        message.writeTo(new FileOutputStream("保存路径"));
*/
/*
带附件
       MimeBodyPart text = new MimeBodyPart();
        text.setContent("使用JavaMail创建的带附件的邮件", "text/html;charset=UTF-8");
        // 创建邮件附件
        MimeBodyPart attach = new MimeBodyPart();
        DataHandler dh = new DataHandler(new FileDataSource("src/2.jpg"));
        attach.setDataHandler(dh);
        attach.setFileName(dh.getName());  //
        // 创建容器描述数据关系
        MimeMultipart mp = new MimeMultipart();
        mp.addBodyPart(text);
        mp.addBodyPart(attach);
        mp.setSubType("mixed");
        //
        message.setContent(mp);
        message.saveChanges();
        message.writeTo(new FileOutputStream("保存路径"));
*/
/*
图片+附件
        // 正文
        MimeBodyPart text = new MimeBodyPart();
        text.setContent("xxx这是女的xxxx<br/><img src='cid:aaa.jpg'>","text/html;charset=UTF-8");
        // 图片
        MimeBodyPart image = new MimeBodyPart();
        image.setDataHandler(new DataHandler(new FileDataSource("src/3.jpg")));
        image.setContentID("aaa.jpg");
        // 附件1
        MimeBodyPart attach = new MimeBodyPart();
        DataHandler dh = new DataHandler(new FileDataSource("src/4.zip"));
        attach.setDataHandler(dh);
        attach.setFileName(dh.getName());
        // 附件2
        MimeBodyPart attach2 = new MimeBodyPart();
        DataHandler dh2 = new DataHandler(new FileDataSource("src/hello.zip"));
        attach2.setDataHandler(dh2);
        attach2.setFileName(MimeUtility.encodeText(dh2.getName()));
        // 描述关系:正文和图片
        MimeMultipart mp1 = new MimeMultipart();
        mp1.addBodyPart(text);
        mp1.addBodyPart(image);
        mp1.setSubType("related");
        // 描述关系:正文和附件
        MimeMultipart mp2 = new MimeMultipart();
        mp2.addBodyPart(attach);
        mp2.addBodyPart(attach2);
        // 代表正文的bodypart
        MimeBodyPart content = new MimeBodyPart();
        content.setContent(mp1);
        mp2.addBodyPart(content);
        mp2.setSubType("mixed");
        //
        message.setContent(mp2);
        message.saveChanges();
        message.writeTo(new FileOutputStream("本地保存路径"));
*/
        return message;
    }
}
  1. 如果连接失败
    检查账号密码
    打开发件邮箱,开启POP3/SMTP服务、IMAP/SMTP服务

2. 文件上传

文件上传:将文件以流的形式提交到服务器。

注意:
    1、为保证文件安全,上传文件应该放在外界无法直接访问的目录下,比如放于WEB-INF目录下。
    2、为防止文件覆盖,要为上传文件产生一个唯一的文件名。
    3、为防止一个目录下出现太多文件,要使用hash算法打散存储。
    4、要限制上传文件的最大值。
    5、要限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法。

// 存储文件位于/Users/用户名/Workspaces/MyEclipse 2015/.metadata/.me_tcat7/webapps/项目名/下

示例

  1. 下载并导入jar依赖包。

在Apache官网下载common-fileupload.jarcommon-io.jar 依赖包,存放到WEB-INF的lib目录下

  1. 上传文件jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    <title>My JSP 'Upload.jsp' starting page</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">
    <!--
    <link rel="stylesheet" type="text/css" href="styles.css">
    -->
  </head>
  <body>
   <form action="${pageContext.request.contextPath}/servlet/UploadServlet" enctype="multipart/form-data" method="post">
        上传用户:<input type="text" name="username"><br/>
        上传文件1:<input type="file" name="file1"><br/>
        上传文件2:<input type="file" name="file2"><br/>
        <input type="submit" value="提交">
    </form>
  </body>
</html>
  1. 上传文件处理类
package com.sst.cx;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
@WebServlet("/UploadServlet")
public class UploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    public UploadServlet() {
        super();
    }
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
                // 上传文件的保存目录。
                String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");
                // 上传时生成的临时文件的保存目录
                String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp");
                File tmpFile = new File(tempPath);
                if (!tmpFile.exists()) {
                    // 创建临时目录
                    tmpFile.mkdir();
                }
                // 消息提示
                String message = "";
                try{
                    // 使用Apache的common-fileupload文件上传组件上传文件:
                    // 1、创建一个DiskFileItemFactory工厂
                    DiskFileItemFactory factory = new DiskFileItemFactory();
                    // 设置缓冲区的大小为100KB(默认是10KB)。当上传的文件大小超过缓冲区的大小时,就会生成一个临时文件存放到指定的临时目录当中。
                    factory.setSizeThreshold(1024*100);
                    // 设置上传时生成的临时文件的保存目录
                    factory.setRepository(tmpFile);
                    // 2、创建一个文件上传解析器
                    ServletFileUpload upload = new ServletFileUpload(factory);
                    // 监听文件上传进度
                    upload.setProgressListener(new ProgressListener(){
                        public void update(long pBytesRead, long pContentLength, int arg2) {
                            System.out.println("文件大小为:" + pContentLength + ",当前已处理:" + pBytesRead);
                            /**
                             * 文件大小为:14608,当前已处理:4096
                                文件大小为:14608,当前已处理:7367
                                文件大小为:14608,当前已处理:11419
                                文件大小为:14608,当前已处理:14608
                             */
                        }
                    });
                     // 解决上传文件名的中文乱码
                    upload.setHeaderEncoding("UTF-8"); 
                    // 3、判断提交上来的数据是否是上传表单的数据
                    if(!ServletFileUpload.isMultipartContent(request)){
                        // 按照传统方式获取数据
                        return;
                    }
                    // 设置上传单个文件大小的最大值为1024*1024字节,即1MB
                    upload.setFileSizeMax(1024*1024);
                    // 设置上传文件总量的最大值为10MB,最大值=同时上传的多个文件的大小的最大值的和
                    upload.setSizeMax(1024*1024*10);
                    // 4、使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每一个FileItem对应一个Form表单的输入项
                    List<FileItem> list = upload.parseRequest(request);
                    for(FileItem item : list){
                        // 如果fileitem中封装的是普通输入项的数据
                        if(item.isFormField()){
                            String name = item.getFieldName();
                            // 解决普通输入项的数据的中文乱码问题
                            String value = item.getString("UTF-8");
                            //value = new String(value.getBytes("iso8859-1"),"UTF-8");
                            System.out.println(name + "=" + value);
                        }else{ // 如果fileitem中封装的是上传文件
                            // 得到上传的文件名称
                            String filename = item.getName();
                            System.out.println(filename);
                            if(filename==null || filename.trim().equals("")){
                                continue;
                            }
                            // 注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如:  /a/b/1.txt,而有些只是单纯的文件名,如:1.txt
                            // 处理获取到的上传文件的文件名的路径部分,只保留文件名部分
                            filename = filename.substring(filename.lastIndexOf("/")+1);
                            // 得到上传文件的扩展名(如果需要限制上传的文件类型,那么可以通过文件的扩展名来判断上传的文件类型是否合法)
                            String fileExtName = filename.substring(filename.lastIndexOf(".")+1);
                            // 获取item中的上传文件的输入流
                            InputStream in = item.getInputStream();
                            // 得到文件保存的名称
                            String saveFilename = makeFileName(filename);
                            // 得到文件的保存目录
                            String realSavePath = makePath(saveFilename, savePath);
                            // 创建一个文件输出流
                            FileOutputStream out = new FileOutputStream(realSavePath + "/" + saveFilename);
                            // 创建一个缓冲区
                            byte buffer[] = new byte[1024];
                            // 判断输入流中的数据是否已经读完的标识
                            int len = 0;
                            // 循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据
                            while((len=in.read(buffer))>0){
                                // 使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(savePath + "/" + filename)当中
                                out.write(buffer, 0, len);
                            }
                            // 关闭输入流
                            in.close();
                            // 关闭输出流
                            out.close();
                            // 删除处理文件上传时生成的临时文件
                            item.delete();
                            message = "文件上传成功!";
                        }
                    }
                }catch (FileUploadBase.FileSizeLimitExceededException e) {
                    e.printStackTrace();
                    request.setAttribute("message", "单个文件超出最大值!!!");
                    request.getRequestDispatcher("/message.jsp").forward(request, response);
                    return;
                }catch (FileUploadBase.SizeLimitExceededException e) {
                    e.printStackTrace();
                    request.setAttribute("message", "上传文件的总的大小超出限制的最大值!!!");
                    request.getRequestDispatcher("/message.jsp").forward(request, response);
                    return;
                }catch (Exception e) {
                    message= "文件上传失败!";
                    e.printStackTrace();
                }
                request.setAttribute("message",message);
                request.getRequestDispatcher("/message.jsp").forward(request, response);
    }
    /**
    生成上传文件的文件名,文件名以:uuid+"_"+文件的原始名称
    */ 
    private String makeFileName(String filename){  
        return UUID.randomUUID().toString() + "_" + filename;
    }
    /**
    返回新的存储目录(在上传文件的保存目录中,使用hash算法打散存储)
    */ 
    private String makePath(String filename,String savePath){
        // 得到文件名的hashCode的值(filename这个字符串对象在内存中的地址)
        int hashcode = filename.hashCode();
        int dir1 = hashcode&0xf; 
        int dir2 = (hashcode&0xf0)>>4;
        // 构造新的保存目录
        String dir = savePath + "/" + dir1 + "/" + dir2;  
        // File既可以代表文件也可以代表目录
        File file = new File(dir);
        // 如果目录不存在
        if(!file.exists()){
            // 创建目录
            file.mkdirs();
        }
        return dir;
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }
}

3. 文件下载

示例

  1. 获取要下载的文件列表处理类(从之前上传文件的目录中获取)
package com.sst.cx;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/FileListServlet")
public class FileListServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
     public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            // 获取上传文件的目录
            String uploadFilePath = this.getServletContext().getRealPath("/WEB-INF/upload");
            // 存储要下载的文件名
            Map<String,String> fileNameMap = new HashMap<String,String>();
            // 递归遍历filepath目录下的所有文件和目录,将文件的文件名存储到map集合中
            listfile(new File(uploadFilePath),fileNameMap);// File既可以代表一个文件也可以代表一个目录
            // 将Map集合发送到listfile.jsp页面进行显示
            request.setAttribute("fileNameMap", fileNameMap);
            request.getRequestDispatcher("/fileList.jsp").forward(request, response);
        }
        /**
            递归遍历指定目录下的所有文件,添加到map中
        */ 
        public void listfile(File file,Map<String,String> map){
            // 如果file代表的不是一个文件,而是一个目录
            if(!file.isFile()){
                // 列出该目录下的所有文件和目录
                File files[] = file.listFiles();
                // 遍历files[]数组
                for(File f : files){
                    // 递归
                    listfile(f,map);
                }
            }else{
                 // 还原文件名(去除文件名的uuid_部分)
                String realName = file.getName().substring(file.getName().indexOf("_")+1);
                map.put(file.getName(), realName);
            }
        }   
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }
}
  1. 文件下载处理类
package com.sst.cx;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/DownloadServlet")
public class DownloadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 得到要下载的文件名
        String fileName = request.getParameter("filename");  
        fileName = new String(fileName.getBytes("iso8859-1"),"UTF-8");
        // 之前上传的文件都是保存在/WEB-INF/upload目录下的子目录当中
        String fileSaveRootPath=this.getServletContext().getRealPath("/WEB-INF/upload");
        // 通过唯一文件名找出文件的所存储的目录
        String path = findFileSavePathByFileName(fileName,fileSaveRootPath);
        // 得到要下载的文件
        File file = new File(path + "/" + fileName);
        // 如果文件不存在
        if(!file.exists()){
            request.setAttribute("message", "您要下载的资源已被删除!!");
            request.getRequestDispatcher("/message.jsp").forward(request, response);
            return;
        }
        // 处理文件名
        String realname = fileName.substring(fileName.indexOf("_")+1);
        // 设置响应头,控制浏览器下载该文件
        response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(realname, "UTF-8"));
        // 读取要下载的文件,保存到文件输入流
        FileInputStream in = new FileInputStream(path + "/" + fileName);
        // 创建输出流
        OutputStream out = response.getOutputStream();
        // 创建缓冲区
        byte buffer[] = new byte[1024];
        int len = 0;
        // 循环将输入流中的内容读取到缓冲区当中
        while((len=in.read(buffer))>0){
            // 输出缓冲区的内容到浏览器,实现文件下载
            out.write(buffer, 0, len);
        }
        // 关闭文件输入流
        in.close();
        // 关闭输出流
        out.close();
    }
    /**
    通过文件名和存储上传文件根目录找出要下载的文件的所在路径
    */ 
    public String findFileSavePathByFileName(String filename,String saveRootPath){
        int hashcode = filename.hashCode();
        int dir1 = hashcode&0xf;  
        int dir2 = (hashcode&0xf0)>>4;  
        String dir = saveRootPath + "/" + dir1 + "/" + dir2;  
        return dir;
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }
}
  1. 文件下载列表页jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    <title>My JSP 'fileList.jsp' starting page</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">
    <!--
    <link rel="stylesheet" type="text/css" href="styles.css">
    -->
  </head>
  <body>
      <c:forEach var="me" items="${fileNameMap}">
        <c:url value="/servlet/DownloadServlet" var="downurl">
            <c:param name="filename" value="${me.key}"></c:param>
        </c:url>
        ${me.value}<a href="${downurl}">下载</a>
        <br/>
        </c:forEach>
  </body>
</html>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容