You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用JakartaMail实现邮件附件保存与展示的最佳方案咨询

实现方案

存储逻辑说明

  • 不需要统一转换为Bitmap格式存储:Bitmap仅适用于图片类型附件,PDF、文档、压缩包等其他格式附件无法通过Bitmap存储,最优方案是直接读取附件的原始二进制流写入内部存储,完全保留文件原始格式,避免转换产生的性能损耗和信息丢失。
  • 存储路径选择应用专属内部存储目录,不需要申请额外读写权限,数据仅当前应用可访问,安全性更高,写入完成后将完整文件路径存入本地数据库即可后续调用。

具体代码实现

首先实现附件写入内部存储的工具方法:

// 传入Context和邮件BodyPart,返回保存后的绝对路径,保存失败返回null
private fun saveAttachmentToInternalStorage(context: Context, bodyPart: BodyPart): String? {
    // 处理附件文件名,替换非法字符避免写入失败
    val originalFileName = bodyPart.fileName?.replace(Regex("[\\\\/:*?\"<>|]"), "_") ?: return null
    // 创建专属附件存储目录
    val attachmentDir = File(context.filesDir, "mail_attachments")
    if (!attachmentDir.exists()) {
        attachmentDir.mkdirs()
    }
    val targetFile = File(attachmentDir, originalFileName)
    return runCatching {
        // 直接读输入流写入文件,不需要格式转换
        bodyPart.inputStream.use { input ->
            targetFile.outputStream().use { output ->
                input.copyTo(output)
            }
        }
        targetFile.absolutePath
    }.getOrNull()
}

修改原有的邮件解析方法,补充附件处理逻辑:

// 此处将返回值第一个参数调整为附件路径列表,适配多附件场景;如果需要返回字符串可自行用分隔符拼接列表
private fun parseMailContent(context: Context, multipart: Multipart): Pair<List<String>, MessageContent> {
    val messageContent = MessageContent()
    val attachmentPaths = mutableListOf<String>()

    for (i in 0 until multipart.count) {
        val bodyPart: BodyPart = multipart.getBodyPart(i)
        val disposition: String? = bodyPart.disposition

        // 处理附件逻辑
        if (disposition != null && disposition.equals("ATTACHMENT", ignoreCase = true)) {
            saveAttachmentToInternalStorage(context, bodyPart)?.let { path ->
                attachmentPaths.add(path)
            }
            // 附件不需要解析文本,直接跳过后续逻辑
            continue
        }

        // 处理文本内容
        val contentType = bodyPart.contentType
        when {
            contentType.contains("TEXT/PLAIN", ignoreCase = true) -> {
                messageContent.text += bodyPart.content.toString()
            }
            contentType.contains("TEXT/HTML", ignoreCase = true) -> {
                messageContent.html += bodyPart.content.toString()
            }
            // 原逻辑中的else分支会清空已解析的正文,建议删除,避免内嵌资源等非文本非附件内容误清空正文
        }
    }

    return Pair(attachmentPaths, messageContent)
}

附件展示逻辑

后续读取数据库中存储的文件路径生成File对象后,按文件后缀/MIME类型匹配对应展示方式即可:

  • 图片类附件:直接用Coil、Glide等图片加载框架加载File对象
  • PDF类附件:调用系统PDF阅读器或第三方PDF加载库加载File对象
  • 其他格式附件同理,按MIME类型匹配对应打开工具即可

内容的提问来源于stack exchange,提问作者Antonio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.12 20:09:03