如何在Flutter中使用enough_mail从MimeMessage对象下载附件
Dart Flutter MimeMessage附件提取下载实现
前置依赖
你需要在pubspec.yaml中添加以下依赖:
- enough_mail:邮件解析核心库,提供MimeMessage相关操作API
- path_provider:用于获取设备存储路径
- permission_handler:用于申请存储权限
- device_info_plus:用于判断安卓系统版本适配权限逻辑(可选,可按需删除相关代码)
完整实现代码
import 'dart:io'; import 'package:flutter/material.dart'; import 'package:enough_mail/enough_mail.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:device_info_plus/device_info_plus.dart'; class EmailScreen extends StatefulWidget { EmailScreen({ Key? key, required this.mimeMessage, required this.userInfo }) : super(key: key); final MimeMessage mimeMessage; final UserInfo userInfo; @override EmailScreenState createState() => EmailScreenState( mimeMessage: mimeMessage, userInfo: userInfo ); } class EmailScreenState extends State<EmailScreen> { MimeMessage mimeMessage; UserInfo userInfo; EmailScreenState({Key? key, required this.mimeMessage, required this.userInfo}); // 附件列表状态 List<MimePart> _attachments = []; bool _isLoading = true; String _operateTip = ''; @override void initState() { super.initState(); _extractAttachments(); } // 提取所有附件 void _extractAttachments() { _attachments = mimeMessage.findContentInfo() .where((contentInfo) => contentInfo.isAttachment) .map((contentInfo) => contentInfo.part) .toList(); setState(() { _isLoading = false; }); } // 下载保存附件到本地 Future<void> _saveAttachment(MimePart attachment) async { // 存储权限申请逻辑 PermissionStatus permissionStatus; if (Platform.isAndroid) { final androidInfo = await DeviceInfoPlugin().androidInfo; if (androidInfo.version.sdkInt >= 33) { permissionStatus = await Permission.photos.request(); } else { permissionStatus = await Permission.storage.request(); } } else if (Platform.isIOS) { permissionStatus = await Permission.photosAddOnly.request(); } else { permissionStatus = PermissionStatus.granted; } if (!permissionStatus.isGranted) { setState(() { _operateTip = '存储权限被拒绝,无法保存附件'; }); return; } // 获取附件信息 final fileName = attachment.fileName ?? 'unnamed_attachment_${DateTime.now().millisecondsSinceEpoch}'; final fileContent = attachment.decodeContentBinary(); if (fileContent == null) { setState(() { _operateTip = '附件内容解析失败,下载终止'; }); return; } // 确定保存路径 Directory? saveDirectory; if (Platform.isAndroid) { saveDirectory = await getExternalStorageDirectory(); saveDirectory ??= await getTemporaryDirectory(); } else { saveDirectory = await getApplicationDocumentsDirectory(); } final savePath = '${saveDirectory.path}/$fileName'; final targetFile = File(savePath); await targetFile.writeAsBytes(fileContent); setState(() { _operateTip = '附件已成功保存到:$savePath'; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('邮件详情')), body: _isLoading ? const Center(child: CircularProgressIndicator()) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (_operateTip.isNotEmpty) Padding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Text(_operateTip, style: const TextStyle(color: Colors.green, fontSize: 14)), ), if (_attachments.isEmpty) const Expanded( child: Center(child: Text('当前邮件没有附件')) ) else Expanded( child: ListView.builder( padding: const EdgeInsets.all(8), itemCount: _attachments.length, itemBuilder: (context, index) { final currentAttach = _attachments[index]; final sizeInKb = (currentAttach.size ?? 0) / 1024; return ListTile( leading: const Icon(Icons.attachment, color: Colors.blue), title: Text(currentAttach.fileName ?? '未命名附件'), subtitle: Text('${sizeInKb.toStringAsFixed(2)} KB'), trailing: IconButton( icon: const Icon(Icons.download, color: Colors.green), onPressed: () => _saveAttachment(currentAttach), ), ); }, ) ) ], ), ); } }
注意事项
- 如果你使用的是其他邮件库的MimeMessage对象,只需要调整
_extractAttachments方法中的附件提取逻辑即可,下载保存逻辑可以通用 - 如需打开下载完成的附件,可以添加
open_file依赖调用打开方法 - 公共下载目录的获取可以用
getDownloadsDirectory()方法替换,按需调整即可
内容的提问来源于stack exchange,提问作者Shahariar Kabir
相关产品推荐
相关产品推荐

