资讯详情

资讯详情

imessage-exporter 中 iMessage 数据库 attachment 表结构全解:字段语义、路径解析与导出链路

CLI即时通讯【免费下载链接】imessage-exporterExport iMessage data run iMessage Diagnostics项目地址https://gitcode.com/gh_mirrors/im/imessage-exporter点击查看免费下载本篇文章以 imessage-exporter 项目官方维护的 attachment 表结构文档 为骨架结合 attachment 表实现源码 与导出器代码系统梳理 macOS/iOS 上 Messages 应用attachment表的 24 个字段含义、附件文件在磁盘上的路径解析规则、贴纸与 Genmoji 元数据的解码方式以及附件在 txt/html 导出链路中的完整流转。读完本文你将能独立读懂任意一份chat.db/sms.db/ iOS 备份中的附件数据并理解 imessage-exporter 在导出时对附件执行复制—转换—去重—渲染的底层原理。一、attachment 表在 iMessage 数据库中的定位iMessage 消息数据库macOS 为~/Library/Messages/chat.db越狱 iOS 设备为~/Library/SMS/sms.dbiOS 备份中的路径见 table.rs 常量是一张 SQLite 数据库。其中attachment表负责保存每一条消息附带的文件元数据图片、视频、音频、贴纸、App 卡片资源等。从 表名常量定义 可以看到核心表共有 6 张handle、message、chat、attachment、chat_message_join、message_attachment_join。attachment表不直接与message表建立外键而是通过中间的message_attachment_join关联表把一条消息与多个附件连接起来——这意味着一条消息可以携带多条附件一个附件也可以被多条消息引用例如同一张图片转发多次时可能共享记录。pub const ATTACHMENT: str attachment; pub const MESSAGE_ATTACHMENT_JOIN: str message_attachment_join;二、完整字段清单官方 schema以下是 docs/tables/attachment.md 记录的完整表结构cid为列序号notnull为 1 表示不允许 NULLdflt_value为默认值pk为 1 表示主键cidnametypenotnulldflt_valuepk0ROWIDINTEGER011guidTEXT102created_dateINTEGER0003start_dateINTEGER0004filenameTEXT005utiTEXT006mime_typeTEXT007transfer_stateINTEGER0008is_outgoingINTEGER0009user_infoBLOB0010transfer_nameTEXT0011total_bytesINTEGER00012is_stickerINTEGER00013sticker_user_infoBLOB0014attribution_infoBLOB0015hide_attachmentINTEGER00016ck_sync_stateINTEGER00017ck_server_change_token_blobBLOB0018ck_record_idTEXT0019original_guidTEXT1020is_commsafety_sensitiveINTEGER00021emoji_image_content_identifierTEXT0NULL022emoji_image_short_descriptionTEXT0NULL023preview_generation_stateINTEGER000值得注意的是guid与original_guid两列都标记为notnull1ROWID是唯一主键。created_date/start_date等时间列与message表中的date列一样存的是 Apple 的参考日期 2001-01-01 起的秒数Core Data 纪元而非 Unix 时间戳。三、字段分组语义详解3.1 标识字段ROWID / guid / original_guidROWIDSQLite 自增主键导出时它被用作附件在对话目录中的输出文件名见下文去重小节例如7.heic。guid附件唯一标识。它对应消息正文附件占位符中的__kIMFileTransferGUIDAttributeName属性见 attachment_meta.rs导出器依靠它把正文占位符与附件行按身份配对而不是按位置配对。original_guid附件记录的原始 GUID在转发、复制场景下保留最初的身份。3.2 时间字段created_date / start_datecreated_date附件记录创建时间。start_date附件开始传输/显示的时间。两者均为INTEGER默认 0。导出器会把它们格式化为可读时间参考 dates.rs当使用--use-message-times选项时附件文件的创建时间会被设置为对应消息的日期。3.3 文件元数据filename / uti / mime_type / transfer_name / total_bytes这组字段在 Attachment 结构体 中都有对应成员列名Rust 字段含义filenamefilename: OptionString附件在磁盘上的存储路径相对/绝对macOS 通常以~/Library/Messages/Attachments/开头utiuti: OptionStringUniform Type Identifier如public.pngmime_typemime_type: OptionStringMIME 类型字符串如image/png、video/mp4transfer_nametransfer_name: OptionString用户看到的原始文件名total_bytestotal_bytes: i64Messages 记录的附件字节数其中filename()方法attachment.rs#L353优先返回transfer_name缺失时回退到filename保证渲染时总能取到一个可展示的名字pub fn filename(self) - Optionstr { self.transfer_name.as_deref().or(self.filename.as_deref()) }total_bytes通过 format_file_size 转成人类可读的大小测试用例 attachment.rs 测试模块 验证了从100.00 B到8.60 GB、乃至i64::MAX时8388608.00 TB的格式化结果。3.4 传输与 UI 状态transfer_state / is_outgoing / hide_attachment / preview_generation_statetransfer_state附件文件传输状态下载、完成、失败等。is_outgoing是否为发出的附件1 为自己发送0 为接收。hide_attachmentMessages 界面隐藏标志源码注释明确为 Messages UI hide flagattachment.rs#L160。preview_generation_state预览图生成状态。3.5 贴纸与表情专用字段is_sticker / sticker_user_info / emoji_image_*is_sticker是否为贴纸。贴纸在现代 iOS/macOS Messages 里本质是附件通过该标志区分。sticker_user_infoBLOB属性列表plist存储贴纸来源信息。解析函数sticker_info()用plist::Value::from_reader反序列化attachment.rs#L569再读取其中的pidbundle ID来确定贴纸来源。emoji_image_content_identifier/emoji_image_short_descriptionGenmoji 相关字段。其中emoji_image_short_description被映射为Attachment.emoji_description源码注释明确其为 Prompt used to generate a Genmoji生成 Genmoji 的提示词。3.6 归属与 App 信息user_info / attribution_infoattribution_info是另一个 plistBLOB用于记录这个附件来自哪个 App。解析结果AttachmentAttributionattachment.rs#L98-L136包含三个键plist 键类型含义bundle-idString贴纸/附件来源的完整 bundle ID必填缺失则解析为NonenameString?App 显示名adam-idi64?App Store 应用 ID仓库自带两份测试夹具Drawing.plist 验证了画板com.apple.PaperKit.MessagesDrawingBoard的解析Sticker.plist 验证了贴纸 Appcom.freepeople.iosapp-production.stickers、adam-id659532790的解析。3.7 iCloud 同步字段ck_sync_state / ck_server_change_token_blob / ck_record_id这组ck_*字段由 CloudKit 同步使用ck_sync_state同步状态。ck_server_change_token_blob服务端变更令牌BLOB。ck_record_idCloudKit 记录 ID。它们与message表中同名ck_*列对应用于多端同步的去重与增量拉取。3.8 安全字段is_commsafety_sensitive标记附件是否被通信安全Communication Safety机制判定为敏感内容。imessage-exporter 的导出逻辑不会依赖该字段做内容拦截它只是随行数据。四、附件与消息的关联message_attachment_joinAttachment::from_message()attachment.rs#L203-L231是导出器获取某条消息的所有附件的入口实际执行的 SQL 为SELECT a.rowid, a.guid, a.filename, a.uti, a.mime_type, a.transfer_name, a.total_bytes, a.is_sticker, a.hide_attachment, a.emoji_image_short_description FROM message_attachment_join j LEFT JOIN attachment a ON j.attachment_id a.ROWID WHERE j.message_id ?1其中COLS常量定义在 attachment.rs#L49。注意两点返回顺序不保证源码注释明确指出 join 表的顺序与正文中AttributedRange的显示顺序不一定一致因此调用方必须按 file-transfer GUID 配对而不是按位置。message 查询中的计数message表的查询在 query_parts.rs 中通过子查询(SELECT COUNT(*) FROM message_attachment_join a WHERE m.ROWID a.message_id) as num_attachments统计附件数Message::has_attachments()即判断num_attachments 0。五、附件路径解析从 filename 到磁盘文件filename列里存的是 Messages 数据库视角的路径imessage-exporter 需要把它解析为宿主机上真实存在的文件。核心函数是 resolved_attachment_path按平台分两条路径5.1 macOS展开~前缀fn gen_macos_attachment(path: str) - String { if path.starts_with(~) { return path.replacen(~, home(), 1); } path.to_string() }即把~/Library/Messages/Attachments/...展开为真实主目录下的绝对路径。默认根常量定义在 attachment.rs#L39-L48pub const DEFAULT_MESSAGES_ROOT: str ~/Library/Messages; pub const DEFAULT_ATTACHMENT_ROOT: str ~/Library/Messages/Attachments; pub const DEFAULT_STICKER_CACHE_ROOT: str ~/Library/Messages/StickerCache; pub const DEFAULT_SMS_ROOT: str ~/Library/SMS; // 越狱 iOS sms.db5.2 iOS 加密备份SHA-1 哈希重映射加密的 Finder/Apple Devices/iTunes 备份中附件文件被重命名并平铺存储文件名是MediaDomain- 原始相对路径的 SHA-1 十六进制摘要前两位作为目录名fn gen_ios_attachment(file_path: str, db_path: Path) - OptionString { let input file_path.get(2..)?; let digest Sha1::digest(format!(MediaDomain-{input}).as_bytes()); let filename digest.iter().map(|byte| format!({:02x}, byte)).collect::String(); let directory filename.get(0..2)?; Some(format!({}/{directory}/{filename}, db_path.display())) }测试用例can_get_resolved_path_iosattachment.rs#L871-L879验证了a/b/c.png会被解析为db_path/41/41746ffc65924078eae42725c979305626f57cca。5.3 自定义附件根目录--attachment-root当附件数据与数据库默认位置分离时例如从旧备份手工恢复附件可用-r/--attachment-root指定替代根目录。apply_custom_root()attachment.rs#L529-L541只替换开头的 Messages/SMS 根前缀因此同时影响Attachments与StickerCache两个目录~/Library/Messages/Attachments/3d/... → /custom/path/Attachments/3d/... ~/Library/Messages/StickerCache/ab/... → /custom/path/StickerCache/ab/...三条重要边界均有测试用例佐证对 iOS 备份无效resolved_attachment_path只在Platform::macOS时应用自定义根can_get_resolved_path_ios_custom_ignores_prefixed_pathCLI 层也会在 options.rs#L256-L262 打印警告。越狱 iOS sms.db 应使用--platform macOSsms.db采用 macOS 风格的直接文件路径源码注释明确其应走 macOS 解析而非 iOS 备份解析can_get_resolved_path_ios_smsdb测试。自定义根必须是已存在的绝对路径在 options.rs#L246-L254 中有存在性校验。六、MIME 类型分类MediaType 枚举导出器需要按媒体类别决定渲染方式图片嵌入、视频播放、音频播放或下载链接。MediaType 枚举 把 MIME 字符串归类为六种变体示例重建后的 MIMEImage(subtype)image/pngimage/pngVideo(subtype)video/mp4video/mp4Audio(subtype)audio/x-m4aaudio/x-m4aText(subtype)text/plaintext/plainApplication(subtype)application/pdfapplication/pdfOther(mime)fake/bloop原样返回Unknown无 MIME 且 UTI 不识别空字符串分类逻辑mime_type()attachment.rs#L233-L264按category/subtype切分当 MIME 缺失时会尝试用 UTI 兜底例如com.apple.coreaudio-formatCore Audio 格式的语音消息被归类为Audio(x-caf; codecsopus)。相关测试覆盖了 PNG/HEIC/伪造类型/缺失类型四种情况attachment.rs#L718-L743。is_animated_sticker()attachment.rs#L270-L277则判定贴纸是否为动图HEIC 序列image/heics、image/heic-sequence或视频 MIME如video/quicktime的动态 Memoji为 true静态 HEIC/PNG 为 false——这直接影响导出时是否触发 ffmpeg 转码。七、贴纸溯源STICKER_USER_INFO 与 attribution 的联合解码贴纸是附件表里最复杂的一类。imessage-exporter 通过 sticker.rs 中的StickerSource枚举把贴纸分成四类pub enum StickerSource { Genmoji, // com.apple.messages.genmoji Memoji, // com.apple.Animoji.StickersApp.MessagesExtension 等 UserGenerated, // com.apple.Stickers.UserGenerated.MessagesExtension App(String), // 其他贴纸 App 的 bundle ID }来源判定流程get_sticker_decoration()attachment.rs#L626-L650依次串联get_sticker_source()读取sticker_user_infoplist 中的pid→StickerSourceget_sticker_source_application_name()从attribution_info读取 App 显示名get_sticker_effect()读取贴纸文件本体从 HEIC 的 EXIF 字节中提取stickerEffect:type...片段映射为Normal/Outline/Comic/Puffy/Shiny解析实现见 sticker.rs#L121-L152配套测试夹具为 no_effect.heic、outline.heic、comic.heic、puffy.heic、shiny.heic。最终得到StickerDecorationGenmoji 输出用户提示词、Memoji 无附加数据、用户自制贴纸输出特效、App 贴纸输出应用名找不到名字时回退 bundle ID。八、导出链路attachment 表数据如何变成导出文件8.1 附件管理模式--copy-method导出时附件是否拷贝、是否转码由 AttachmentManagerMode 决定CLI 参数为-c/--copy-method可选值disabled默认、clone、basic、full模式行为外部依赖disabled默认不拷贝附件只输出路径引用无clone原样拷贝所有文件不做任何转换无basic拷贝全部文件并将 HEIC 图片转换为 JPEG图片转换器macOS 用sips其他平台用 ImageMagickmagickfull拷贝 转换HEIC→JPEG、CAF→MP4、MOV→MP4上述图片转换器 ffmpeg音视频转换器探测逻辑在 models.rsmacOS 优先sips否则找magick音频在 macOS 用afconvert音视频统一用ffmpeg。若找不到相应转换器会打印警告并回退到原始拷贝copy_raw。8.2 计划—执行Plan 与 AttachmentTarget每个附件在 prepare_attachment 中先被规划Plan再执行pub(crate) enum Plan { Skip, // 拷贝被禁用 Reuse { path: PathBuf, media_type: OptionMediaTypestatic }, // 已有输出直接复用 Process { target: AttachmentTarget }, // 需要真正拷贝/转换 }AttachmentTargettarget.rs在规划阶段就解析好源路径与目标目录保证去重探测与实际拷贝使用同一命名规则。输出文件名规则为对话目录/rowid.源扩展名——rowid 被用作输出文件名这正是第一节提到的 ROWID 在导出中的实际用途。去重逻辑existing_copy()target.rs#L84-L105先探测源扩展名文件原始拷贝再探测转换后的扩展名jpeg/png/gif/mp4命中即复用跨模式重导也能复用已有兄弟文件。这部分有完整的单测覆盖raw_copy_returns_path_and_leaves_mime、converted_image_rebuilds_jpeg_mime、converted_audio_keeps_audio_category等见 target.rs#L135-L325。8.3 正文占位符 ↔ 附件行的配对AttachmentResolver消息正文attributedBodytypedstream 编码中的附件占位符通过\u{FFFC}字符标记body.rs#L45并携带__kIMFileTransferGUIDAttributeName等键完整键集合见 body.rs#L35-L43。渲染时由 AttachmentResolver 负责配对优先按 GUID 配对guid → 附件列表下标的 HashMap 查找旧版无 GUID 的正文回退按位置配对next_positional游标顺序消费。该设计规避了 join 查询顺序与正文显示顺序不一致的问题保证多附件消息渲染不错位。8.4 渲染模板HTML 与 TXTHTML 附件模板 根据附件类别生成对应标签图片img src... loadinglazy-l/--no-lazy可移除懒加载视频video controlssource type...音频audio controls带转写时额外渲染Transcription: ...转写文本来自正文 range 上的IMAudioTranscription键见 attachment_meta.rs其他类型下载链接、文件夹入口或无法嵌入提示。TXT 附件模板 则只输出路径与转写文本。九、附件诊断run_diagnosticimessage-exporter 的-d/--diagnostics模式会逐表体检。Attachment::run_diagnostic()attachment.rs#L456-L526遍历所有行的filename逐条解析并检查磁盘文件是否存在产出AttachmentDiagnosticdiagnostic.rs指标含义total_attachmentsattachment 表总行数total_bytes_referenced表内total_bytes之和Data referenced in tabletotal_bytes_on_disk按解析路径统计的真实磁盘字节数Data present on diskmissing_files无路径 有路径但文件不存在的行数no_path_provided完全没有路径的行数no_file_located有路径但文件缺失的行数missing_files - no_path_provided输出示例取自 docs/diagnostics.mdAttachment diagnostic data: Total attachments: 49422 Data referenced in table: 44.13 GB Data present on disk: 31.31 GB Missing files: 15037 (30%) No path provided: 14929 No file located: 108如文档所述No path provided 通常是 iCloud 未下载或记录异常的附件No file located 则是文件被用户删除或未随备份完整恢复。此外get_total_attachment_bytes()attachment.rs#L368-L392支持带QueryContext过滤条件起止日期、chat/handle 选择统计附件总字节数用于导出前的磁盘空间预检。十、实操速查围绕 attachment 表的常用命令以 options.rs 中注册的 CLI 参数为准与附件强相关的典型用法# 默认导出不拷贝附件仅引用路径 imessage-exporter -f txt # 原样拷贝全部附件 imessage-exporter -f html -c clone # 拷贝并把 HEIC 转 JPEGbasic/ 全格式转换full imessage-exporter -f html -c basic imessage-exporter -f html -c full # 附件存放在自定义根目录时 imessage-exporter -f html -c clone -r /custom/path # 针对加密 iOS 备份--platform ios 可省略自动判定 imessage-exporter -f txt -a ios -p /path/to/backup -x yourpassword # 只跑数据库体检含附件完整性统计 imessage-exporter -d约束提醒-c/-r/-s/-e等选项必须搭配-f使用-d与导出类选项互斥-r对 iOS 备份无效CLI 会打印警告-x仅适用于 iOS 加密备份。总结attachment表是 iMessage 数据导出与诊断的关键一环guid负责跨表身份配对filename 平台规则决定磁盘定位is_sticker/sticker_user_info/attribution_info支撑贴纸与 Genmoji 的富渲染total_bytes驱动空间预估与诊断统计。理解这张表的 24 个字段就等于掌握了从chat.db原始记录到最终导出文件中可点击、可播放、可下载附件之间的全部秘密。若需继续深入可进一步阅读仓库中的 messages 表结构、chat 表结构 与 handle 表结构或直接在 attachment.rs 的测试模块 中观察每个行为的断言细节。赞分享CLI即时通讯【免费下载链接】imessage-exporterExport iMessage data run iMessage Diagnostics项目地址https://gitcode.com/gh_mirrors/im/imessage-exporter点击查看免费下载相关推荐imessage-exporter 完全指南iMessage 数据导出与数据库诊断的实践与原理imessage exporter 完全指南iMessage 数据导出与数据库诊断的实践与原理 imessage exporter 是一个以 Rust 编写的CLI即时通讯掌握你的iMessage数据imessage-exporter全解析掌握你的iMessage数据imessage exporter全解析 在数字化时代我们日常的通信记录是宝贵的个人资料。imessage exporter是一CLI即时通讯提升Python Web应用性能python-react服务端渲染终极方案提升Python Web应用性能python react服务端渲染终极方案 想要让你的Python Web应用拥有闪电般的加载速度吗 还在为React应后端上一篇Relay useMutation Hook 完全指南在 React 组件中执行 GraphQL Mutation下一篇lark-cli 命令 E2E 覆盖率文件coverage.md规范从 demo 模板到真实域落地创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →