目录
1. 发邮件
2. 上传
3. 下载
本篇笔记是从个人笔记中摘录,不属于教程
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对象。
*/
- 简单使用
下载并导入所需的jar包
在Oracle官网下载所需的mail.jar包、activation.jar包,放在WEB-INF的lib目录下相关文件
注册页
<%@ 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;
}
}
- 如果连接失败
检查账号密码
打开发件邮箱,开启POP3/SMTP服务、IMAP/SMTP服务
2. 文件上传
文件上传:将文件以流的形式提交到服务器。
注意:
1、为保证文件安全,上传文件应该放在外界无法直接访问的目录下,比如放于WEB-INF目录下。
2、为防止文件覆盖,要为上传文件产生一个唯一的文件名。
3、为防止一个目录下出现太多文件,要使用hash算法打散存储。
4、要限制上传文件的最大值。
5、要限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法。
// 存储文件位于/Users/用户名/Workspaces/MyEclipse 2015/.metadata/.me_tcat7/webapps/项目名/下
示例
- 下载并导入jar依赖包。
在Apache官网下载common-fileupload.jar、common-io.jar 依赖包,存放到WEB-INF的lib目录下
- 上传文件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>
- 上传文件处理类
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. 文件下载
示例
- 获取要下载的文件列表处理类(从之前上传文件的目录中获取)
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);
}
}
- 文件下载处理类
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);
}
}
- 文件下载列表页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>