文件上传
1. 上传对表单限制
- method=”post”
- enctype=”multipart/form-data”
- 表单中需要添加文件表单项:<input type=”file” name=”xxx” />
<h3>${msg}</h3>
<form action="xxx" method="post" enctype="multipart/form-data">
用户名;<input type="text" name="username"/><br/>
照 片:<input type="file" name="zhaoPian"/><br/>
<input type="submit" value="上传"/>
</form>
2. 上传对 Servlet 限制
- 文件上传不能使用 BaseServlet
- request.getParametere(“xxx”); 这个方法在表单为 enctype=”multipart/form-data” 时,它作废了。它永远都返回 null
- ServletInputStream request.getInputStream(); 包含整个请求的体!
3. 上传三步
导入 jar(commons-fileupload)
- commons-fileupload.jar
- commons-io.jar
相关类:
- 工厂:DiskFileItemFactory
- 解析器:ServletFileUpload
-
表单项:FileItem
1. 创建工厂:DiskFileItemFactory factory = new DiskFileItemFactory(); 2. 创建解析器:ServletFileUpload sfu = new ServletFileUpload(factory); 3. 使用解析器来解析 request,得到 FileItem 集合:List<FileItem> fileItemList = sfu.parseRequest(request);
4.FileItem
- boolean isFormField():是否为普通表单项!返回 true 为普通表单项,如果为 false 即文件表单项!
- String getFieldName():返回当前表单项的名称;
- String getString(String charset):返回表单项的值;
- String getName():返回上传的文件名称
- long getSize():返回上传文件的字节数
- InputStream getInputStream():返回上传文件对应的输入流
- void write(File destFile):把上传的文件内容保存到指定的文件中。
- String getContentType(); 获取上传的文件的类型
5. 上传的细节
- 保存到 WEB-INF 下!(目的是不让浏览器直接访问到)
- 有的浏览器上传的文件名是绝对路径,这需要切割
-
目录打散
哈希打散:通过文件名称得到 int 值,即调用 hashCode() 它 int 值转换成 16 进制 0~9, A~F 获取 16 进制的前两位用来生成目录,目录为二层!例如:1B2C3D4E5F,/1/B/ 保存文件。
6. 代码
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
/*
* 上传三步
*/
// 工厂,设置缓存,超过 20k, 向目录保存
DiskFileItemFactory factory = new DiskFileItemFactory(20*1024, new File("F:/f/temp"));
// 解析器
ServletFileUpload sfu = new ServletFileUpload(factory);
//sfu.setFileSizeMax(100 * 1024);// 限制单个文件大小为 100K
//sfu.setSizeMax(1024 * 1024);// 限制整个表单大小为 1M
// 解析,得到 List
try {List<FileItem> list = sfu.parseRequest(request);
FileItem fi = list.get(1);
/*
* 1. 得到文件保存的路径
*/
String root = this.getServletContext().getRealPath("/WEB-INF/files/");
/*
* 2. 生成二层目录
* 1). 得到文件名称
* 2). 得到 hashCode
* 3). 转发成 16 进制
* 4). 获取前二个字符用来生成目录
*/
String filename = fi.getName();// 获取上传的文件名称
/*
* 处理文件名的绝对路径问题
*/
int index = filename.lastIndexOf("\\");
if(index != -1) {filename = filename.substring(index+1);
}
/*
* 给文件名称添加 uuid 前缀,处理文件同名问题
*/
String savename = CommonUtils.uuid() + "_" + filename;
/*
* 1. 得到 hashCode
*/
int hCode = filename.hashCode();
String hex = Integer.toHexString(hCode);
/*
* 2. 获取 hex 的前两个字母,与 root 连接在一起,生成一个完整的路径
*/
File dirFile = new File(root, hex.charAt(0) + "/" + hex.charAt(1));
/*
* 3. 创建目录链
*/
dirFile.mkdirs();
/*
* 4. 创建目录文件
*/
File destFile = new File(dirFile, savename);
/*
* 5. 保存
*/
fi.write(destFile);
} catch (FileUploadException e) {if(e instanceof FileUploadBase.FileSizeLimitExceededException) {request.setAttribute("msg", "您上传的文件超出了 100KB!");
request.getRequestDispatcher("/form.jsp").forward(request, response);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();}
}
文件下载
1. 下载就是向客户端响应字节数据!
把一个文件变成字节数组,使用 response.getOutputStream() 来各应给浏览器!!!
2. 下载的要求
两个头一个流!
-
Content-Type:你传递给客户端的文件是什么 MIME 类型,例如:image/pjpeg
通过文件名称调用 ServletContext 的 getMimeType() 方法,得到 MIME 类型!
-
Content-Disposition:它的默认值为 inline,表示在浏览器窗口中打开!attachment;filename=xxx
在 filename= 后面跟随的是显示在下载框中的文件名称!
-
流:要下载的文件数据!
自己 new 一个输入流即可!
3. 代码
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
/*
* 两个头一个流
* 1. Content-Type
* 2. Content-Disposition
* 3. 流:下载文件的数据
*/
String filename = "F:/ 清白之年.mp3";
// 为了使下载框中显示中文文件名称不出乱码!String framename = filenameEncoding("清白之年.mp3", req);
String contentType = this.getServletContext()
.getMimeType(filename);// 通过文件名称获取 MIME 类型
String contentDisposition = "attachment;filename=" + framename;
// 一个流
FileInputStream input = new FileInputStream(filename);
// 设置头
resp.setHeader("Content-Type", contentType);
resp.setHeader("Content-Disposition", contentDisposition);
// 获取绑定了响应端的流
ServletOutputStream output = resp.getOutputStream();
IOUtils.copy(input, output);// 把输入流中的数据写入到输出流中。input.close();}
// 用来对下载的文件名称进行编码的!(通用方案)public static String filenameEncoding(String filename, HttpServletRequest request) throws IOException {String agent = request.getHeader("User-Agent"); // 获取浏览器
if (agent.contains("Firefox")) {BASE64Encoder base64Encoder = new BASE64Encoder();
filename = "=?utf-8?B?"
+ base64Encoder.encode(filename.getBytes("utf-8"))
+ "?=";
} else if(agent.contains("MSIE")) {filename = URLEncoder.encode(filename, "utf-8");
} else {filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
JavaMail
注意:一般邮箱需要开通 POP3/SMTP/IMAP 功能,发过去邮件很可能在垃圾箱里面
1. 导入 jar
mail.jar
activation.jar
2. 主要类
javax.mail.Session
javax.mail.internet.MimeMessage
javax.mail.Transport
3. 代码
- 无附件
@Test
public void fun1() throws Exception {
/*
* 1. 得到 session
*/
Properties props = new Properties();
props.setProperty("mail.host", "smtp.163.com");
props.setProperty("mail.smtp.auth", "true");
Authenticator auth = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {return new PasswordAuthentication("itcast_cxf", "itcast");// 账号密码
}
};
Session session = Session.getInstance(props, auth);
/*
* 2. 创建 MimeMessage
*/
MimeMessage msg = new MimeMessage(session);
msg.setFrom(new InternetAddress("itcast_cxf@163.com"));// 设置发件人
msg.setRecipients(RecipientType.TO, "itcast_cxf@126.com");// 设置收件人
//msg.setRecipients(RecipientType.CC, "itcast_cxf@sohu.com");// 设置抄送
//msg.setRecipients(RecipientType.BCC, "itcast_cxf@sina.com");// 设置暗送
msg.setSubject("这是来自 ITCAST 的测试邮件");
msg.setContent("这就是一封垃圾邮件!", "text/html;charset=utf-8");
/*
* 3. 发
*/
Transport.send(msg);
}
- 有附件
@Test
public void fun2() throws Exception {
/*
* 1. 得到 session
*/
Properties props = new Properties();
props.setProperty("mail.host", "smtp.163.com");
props.setProperty("mail.smtp.auth", "true");
Authenticator auth = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {return new PasswordAuthentication("itcast_cxf", "itcast");
}
};
Session session = Session.getInstance(props, auth);
/*
* 2. 创建 MimeMessage
*/
MimeMessage msg = new MimeMessage(session);
msg.setFrom(new InternetAddress("itcast_cxf@163.com"));// 设置发件人
msg.setRecipients(RecipientType.TO, "itcast_cxf@126.com");// 设置收件人
msg.setSubject("这是来自 ITCAST 的测试邮件有附件");
/*
* 当发送包含附件的邮件时,邮件体就为多部件形式!* 1. 创建一个多部件的部件内容!MimeMultipart
* MimeMultipart 就是一个集合,用来装载多个主体部件!* 2. 我们需要创建两个主体部件,一个是文本内容的,另一个是附件的。* 主体部件叫 MimeBodyPart
* 3. 把 MimeMultipart 设置给 MimeMessage 的内容!*/
MimeMultipart list = new MimeMultipart();// 创建多部分内容
// 创建 MimeBodyPart
MimeBodyPart part1 = new MimeBodyPart();
// 设置主体部件的内容
part1.setContent("这是一封包含附件的垃圾邮件", "text/html;charset=utf-8");
// 把主体部件添加到集合中
list.addBodyPart(part1);
// 创建 MimeBodyPart
MimeBodyPart part2 = new MimeBodyPart();
part2.attachFile(new File("F:/f/ 白冰.jpg"));// 设置附件的内容
part2.setFileName(MimeUtility.encodeText("大美女.jpg"));// 设置显示的文件名称,其中 encodeText 用来处理中文乱码问题
list.addBodyPart(part2);
msg.setContent(list);// 把它设置给邮件作为邮件的内容。/*
* 3. 发
*/
Transport.send(msg);
}