首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 文件上傳下載與郵件收發完整實戰:Servlet + JavaMail 核心代碼

     編輯:Java 相關知識

文件上傳下載與郵件發送是 Web 系統兩大基礎模塊。本文提供生產級完整代碼:基於 Servlet + Commons FileUpload 實現文件上傳(唯一文件名、大小限制、編碼處理)、下載(斷點續傳支持、中文文件名編碼)、列表展示;基於 JavaMail 實現純文本、HTML+內嵌圖片、帶附件三種郵件發送場景,含本地郵件服務器搭建步驟。

一、文件上傳下載模塊

1. 上傳 Servlet

@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);
        }
    }
}

2. 下載列表頁

<%@ 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>

二、JavaMail 郵件發送模塊

環境準備

  • 本地搭建郵件服務器(如 hMailServer、Postfix)或使用 SMTP 服務商(QQ、163、SendGrid)
  • 依賴:javax.mail:mail:1.4.7、javax.activation:activation:1.1.1(JDK 9+ 需額外引入)

1. 純文本郵件

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();
}

2. HTML + 內嵌圖片

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);
    
    // ... 發送 ...
}

3. 圖片 + 附件

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);
    
    // ... 發送 ...
}

三、關鍵最佳實踐

  1. 文件名編碼:上傳用 UUID 防衝突,下載用 URLEncoder.encode,郵件附件用 MimeUtility.encodeText
  2. 流式傳輸:大文件下載/郵件附件使用流複製,避免全量載入內存
  3. SSL/TLS:生產環境強制啟用 mail.smtp.ssl.enable=true 或 STARTTLS
  4. 授權碼而非密碼:主流郵箱需開啟 SMTP 服務並獲取授權碼
  5. 異步發送:郵件發送放入線程池或消息隊列,不阻塞業務線程
  6. 模板引擎:複雜 HTML 郵件使用 Thymeleaf/FreeMarker 渲染

四、總結

文件上傳下載與郵件發送看似基礎,但編碼處理、流式傳輸、安全防範、異步解耦等細節決定系統穩健度。本文代碼經過生產驗證,可直接作為工具類集成至項目,並按需擴展:斷點續傳、圖片壓縮、郵件模板、發送狀態追蹤等。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved