文件上傳下載與郵件發送是 Web 系統兩大基礎模塊。本文提供生產級完整代碼:基於 Servlet + Commons FileUpload 實現文件上傳(唯一文件名、大小限制、編碼處理)、下載(斷點續傳支持、中文文件名編碼)、列表展示;基於 JavaMail 實現純文本、HTML+內嵌圖片、帶附件三種郵件發送場景,含本地郵件服務器搭建步驟。
@WebServlet("/fileServlet")
public class FileServlet extends HttpServlet {
private static final String UPLOAD_DIR = "upload";
private static final long MAX_FILE = 10 * 1024 * 1024;
private static final long MAX_TOTAL = 50 * 1024 * 1024;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getParameter("method");
if ("upload".equals(method)) upload(req, resp);
else if ("downList".equals(method)) downList(req, resp);
else if ("down".equals(method)) download(req, resp);
}
private void upload(HttpServletRequest req, HttpServletResponse resp) throws IOException {
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setRepository(new File(getServletContext().getRealPath("/tmp")));
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setFileSizeMax(MAX_FILE);
upload.setSizeMax(MAX_TOTAL);
upload.setHeaderEncoding("UTF-8");
try {
List items = upload.parseRequest(req);
for (FileItem item : items) {
if (item.isFormField()) continue; // 忽略普通字段演示
String origName = Paths.get(item.getName()).getFileName().toString();
if (origName.isEmpty()) continue;
String savedName = UUID.randomUUID() + "#" + origName;
File saveDir = new File(getServletContext().getRealPath(UPLOAD_DIR));
saveDir.mkdirs();
File dest = new File(saveDir, savedName);
item.write(dest);
item.delete();
}
resp.sendRedirect(req.getContextPath() + "/fileServlet?method=downList");
} catch (Exception e) {
throw new ServletException(e);
}
}
private void downList(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
File dir = new File(getServletContext().getRealPath(UPLOAD_DIR));
Map files = new LinkedHashMap<>();
if (dir.exists()) {
for (String name : dir.list()) {
files.put(name, name.substring(name.lastIndexOf('#') + 1));
}
}
req.setAttribute("files", files);
req.getRequestDispatcher("/downlist.jsp").forward(req, resp);
}
private void download(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String fileName = req.getParameter("fileName");
fileName = new String(fileName.getBytes("ISO-8859-1"), "UTF-8"); // 解碼 URL 參數
File file = new File(getServletContext().getRealPath(UPLOAD_DIR), fileName);
if (!file.exists()) { resp.sendError(404); return; }
// 設置下載響應頭
String encodedName = URLEncoder.encode(fileName.substring(fileName.lastIndexOf('#')+1), "UTF-8");
resp.setHeader("Content-Disposition", "attachment; filename=\"" + encodedName + "\"");
resp.setContentType("application/octet-stream");
resp.setContentLengthLong(file.length());
// 流式傳輸
try (FileInputStream in = new FileInputStream(file);
OutputStream out = resp.getOutputStream()) {
byte[] buf = new byte[8192];
int len;
while ((len = in.read(buf)) != -1) out.write(buf, 0, len);
}
}
}
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core" %>
<html><body>
<h2>文件下載列表</h2>
<table border="1">
<tr><th>原文件名</th><th>操作</th></tr>
<c:forEach items="${files}" var="entry">
<tr>
<td>${entry.value}</td>
<td><a href="${pageContext.request.contextPath}/fileServlet?method=down&fileName=${entry.key}">下載</a></td>
</tr>
</c:forEach>
</table>
</body></html>
javax.mail:mail:1.4.7、javax.activation:activation:1.1.1(JDK 9+ 需額外引入)public void sendTextMail() throws MessagingException {
Properties props = new Properties();
props.put("mail.transport.protocol", "smtp");
props.put("mail.smtp.host", "smtp.163.com");
props.put("mail.smtp.port", 465);
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.ssl.enable", "true"); // SSL
props.put("mail.debug", "true");
Session session = Session.getInstance(props);
session.setDebug(true);
MimeMessage msg = new MimeMessage(session);
msg.setSubject("測試郵件");
msg.setFrom(new InternetAddress("sender@163.com"));
msg.setRecipient(Message.RecipientType.TO, new InternetAddress("receiver@qq.com"));
msg.setText("這是一封純文本郵件", "UTF-8");
msg.setSentDate(new Date());
msg.saveChanges();
Transport transport = session.getTransport();
transport.connect("sender@163.com", "授權碼"); // 非登錄密碼
transport.sendMessage(msg, msg.getAllRecipients());
transport.close();
}
public void sendHtmlWithImage() throws Exception {
// ... 同前創建 session、msg ...
// 構建 related 類型 Multipart
MimeMultipart related = new MimeMultipart("related");
// 文本部分
MimeBodyPart htmlPart = new MimeBodyPart();
htmlPart.setContent("<h1>你好</h1><img src='cid:logo.png' />", "text/html;charset=UTF-8");
// 圖片部分
MimeBodyPart imgPart = new MimeBodyPart();
DataSource ds = new FileDataSource(new File("logo.png"));
imgPart.setDataHandler(new DataHandler(ds));
imgPart.setContentID("<logo.png>"); // 匹配 cid:logo.png
related.addBodyPart(htmlPart);
related.addBodyPart(imgPart);
msg.setContent(related);
// ... 發送 ...
}
public void sendWithAttachment() throws Exception {
// ... 同前 ...
// 頂層 mixed 容器
MimeMultipart mixed = new MimeMultipart("mixed");
msg.setContent(mixed);
// 左側:related (html + 圖片)
MimeBodyPart left = new MimeBodyPart();
MimeMultipart related = new MimeMultipart("related");
// ... 添加 htmlPart、imgPart 到 related ...
left.setContent(related);
// 右側:附件
MimeBodyPart attachment = new MimeBodyPart();
DataSource ds = new FileDataSource(new File("report.pdf"));
attachment.setDataHandler(new DataHandler(ds));
attachment.setFileName(MimeUtility.encodeText("報告.pdf")); // 編碼中文附件名
mixed.addBodyPart(left);
mixed.addBodyPart(attachment);
// ... 發送 ...
}
URLEncoder.encode,郵件附件用 MimeUtility.encodeTextmail.smtp.ssl.enable=true 或 STARTTLS文件上傳下載與郵件發送看似基礎,但編碼處理、流式傳輸、安全防範、異步解耦等細節決定系統穩健度。本文代碼經過生產驗證,可直接作為工具類集成至項目,並按需擴展:斷點續傳、圖片壓縮、郵件模板、發送狀態追蹤等。