如何通过Azure Notification Hub发送带附件的iOS推送及端到端架构设计
嘿,针对你的两个关于Azure Notification Hub和iOS推送的问题,我来给你详细拆解解答:
iOS的推送通知要带媒体附件,本质是用富通知(Rich Notifications),而Azure Notification Hub本身只是负责传递APNs的payload,所以核心是构造符合APNs要求的payload,再配合iOS端的扩展来处理附件下载和展示。具体步骤如下:
第一步:给iOS App添加Notification Service Extension
这个扩展会在通知到达设备时被APNs唤醒,用来下载附件并修改通知内容。你可以在Xcode里通过「File > New > Target」选择「Notification Service Extension」来添加,记得启用这个扩展的权限。第二步:构造正确的APNs Payload
推送的payload必须包含mutable-content: 1(告诉APNs要触发扩展),同时在自定义字段里携带附件的URL(注意:不能直接把文件塞payload里,APNs payload大小限制在4KB左右)。示例payload结构如下:{ "aps": { "alert": { "title": "新附件通知", "body": "请查收你的文档" }, "mutable-content": 1 }, "attachment-url": "https://your-storage-account.blob.core.windows.net/your-container/your-file.pdf?[SAS-Token]" }用Azure SDK发送时(比如C#),可以这样构造:
var appleNotification = new AppleNotification { Alert = new AppleAlert("新附件通知", "请查收你的文档"), MutableContent = true, CustomPayload = new Dictionary<string, string> { { "attachment-url", "你的Blob Storage SAS URL" } } }; await notificationHubClient.SendNotificationAsync(appleNotification, targetTag);第三步:在Extension里处理附件下载
在Notification Service Extension的didReceive方法中,解析payload里的URL,下载文件并添加为通知附件。示例Swift代码:import UserNotifications class NotificationService: UNNotificationServiceExtension { var contentHandler: ((UNNotificationContent) -> Void)? var bestAttemptContent: UNMutableNotificationContent? override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler bestAttemptContent = request.content.mutableCopy() as? UNMutableNotificationContent guard let content = bestAttemptContent, let urlString = content.userInfo["attachment-url"] as? String, let url = URL(string: urlString) else { contentHandler(request.content) return } // 下载附件到临时目录 URLSession.shared.downloadTask(with: url) { location, _, error in guard let location = location, error == nil else { contentHandler(content) return } let tempFile = FileManager.default.temporaryDirectory.appendingPathComponent(url.lastPathComponent) do { try FileManager.default.moveItem(at: location, to: tempFile) let attachment = try UNNotificationAttachment(identifier: "doc", url: tempFile) content.attachments = [attachment] } catch { print("附件处理失败: \(error.localizedDescription)") } contentHandler(content) }.resume() } override func serviceExtensionTimeWillExpire() { contentHandler?(bestAttemptContent ?? request.content) } }
你的思路完全正确:绝对不能把PDF/Word文件直接放进推送payload,必须先存储到Azure Blob Storage,再通过URL让手机端获取。下面是完整的方案细节:
Web App端处理流程
- 用户上传文件时,Web App直接将文件上传到Azure Blob Storage(建议用私有容器,然后生成SAS URL来授权访问,这样可以控制URL的有效期和权限,避免未授权下载)。
- 构造推送payload时,把SAS URL作为自定义字段(比如
document-url)加入,同时设置mutable-content: 1(如果需要在通知栏预览附件的话)。 - 调用Azure Notification Hub的API发送这个payload到目标iOS设备。
手机端处理逻辑
分两种场景:
- 仅需点击通知打开文件:不需要Notification Extension,只要在App的
didFinishLaunchingWithOptions或者userNotificationCenter(_:didReceive:withCompletionHandler:)方法中,解析通知的userInfo拿到URL,然后用UIApplication.shared.open(url)打开,系统会自动调用对应的默认应用(比如Files、Word)来打开PDF/Word文件。 - 通知栏预览附件:就用上面提到的Notification Service Extension,下载文件后生成缩略图添加到通知中,用户在通知栏就能看到预览,点击后再打开完整文件。
最佳架构方案
推荐的架构流程是:
- Web App层:负责用户交互、文件上传到Blob Storage、生成SAS URL、构造推送payload、调用Notification Hub发送通知。
- Azure Blob Storage:存储用户上传的PDF/Word文件,配置私有容器+SAS URL授权,同时可以设置生命周期策略自动清理过期文件(比如30天后删除)。
- Azure Notification Hub:负责与APNs对接,批量推送通知,处理设备注册和标签管理。
- iOS App + Notification Service Extension:处理通知接收、附件预览(可选)、用户点击后的文件打开逻辑。
额外优化建议:
- 在Web App上传文件时,添加病毒扫描(可以用Azure Defender for Storage),避免恶意文件。
- SAS URL设置较短的有效期(比如24小时),如果用户需要长期访问,可以在App内重新请求新的SAS URL。
- 对于大文件,考虑在App内下载时添加进度条,提升用户体验。
内容的提问来源于stack exchange,提问作者Kris RaduhaSt

