From 2f3a35892bbd8e085ea0efde7954dc9fb42ad11e Mon Sep 17 00:00:00 2001 From: joywayer Date: Wed, 24 Jun 2026 18:03:58 +0800 Subject: [PATCH] =?UTF-8?q?4.B=20QQ=20=E9=93=BE=E6=8E=A5=E5=88=86=E4=BA=AB?= =?UTF-8?q?=E8=A1=A5=20previewimageUrl=EF=BC=9A=E7=94=A8=20app=20icon=20?= =?UTF-8?q?=E5=BD=93=E7=BC=A9=E7=95=A5=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QQ 会话里链接分享的缩略图位空白,因为 News 类型缺 previewimageUrl 字段。 对齐 msext QQShareManager.m:517-522 + 1271-1278 的写法: 1. app icon JPEG 0.7 写到 NSTemporaryDirectory()/qq_share_.jpg 2. 拼 file:// 字符串 3. base64 后再 URL-encode 4. append &previewimageUrl=<那串> writeAppIconToTemp() 与 appIconImage() 直接抄 WechatShare 同源策略(读 Info.plist CFBundleIcons → CFBundlePrimaryIcon → CFBundleIconFiles 最后 一个)。两处复制可接受 —— Share 模块统一缩略图工具可后续抽到 ImageProvider,本次保持改动范围最小。 --- ylgamehall/Source/Share/QQShare.swift | 39 +++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/ylgamehall/Source/Share/QQShare.swift b/ylgamehall/Source/Share/QQShare.swift index e37fa0a..f141ee0 100644 --- a/ylgamehall/Source/Share/QQShare.swift +++ b/ylgamehall/Source/Share/QQShare.swift @@ -114,6 +114,13 @@ public final class QQShare: SharePlatform { parts.append("url=\(Self.encode(Self.base64(content.webpageUrl)))") } + // 3. 缩略图 —— app icon 写到 tmp 目录 → previewimageUrl = urlenc(base64("file://")) + // 对齐 msext QQShareManager.m:517-522 saveImageToTempDirectory + base64 + if let thumbPath = Self.writeAppIconToTemp() { + let fileURL = "file://\(thumbPath)" + parts.append("previewimageUrl=\(Self.encode(Self.base64(fileURL)))") + } + let urlString = "mqqapi://\(host)?\(parts.joined(separator: "&"))" Self.log.debug("QQShare URL → \(urlString, privacy: .public)") @@ -184,6 +191,38 @@ public final class QQShare: SharePlatform { return top } + // MARK: - 缩略图(app icon → tmp file) + + /// 把 app icon JPEG 写到 NSTemporaryDirectory,返回绝对路径。 + /// 对齐 msext QQShareManager.m:1271-1278 saveImageToTempDirectory: + /// 文件名 qq_share_.jpg,JPEG 0.7 质量。失败返回 nil。 + private static func writeAppIconToTemp() -> String? { + guard let data = appIconImage().jpegData(compressionQuality: 0.7) else { return nil } + let path = NSTemporaryDirectory() + "qq_share_\(UUID().uuidString).jpg" + do { + try data.write(to: URL(fileURLWithPath: path), options: .atomic) + return path + } catch { + log.error("QQShare 写缩略图临时文件失败 \(error.localizedDescription, privacy: .public)") + return nil + } + } + + /// app icon UIImage,与 WechatShare.appIconImage() 同源策略 + /// (读 Info.plist CFBundleIcons → CFBundlePrimaryIcon → CFBundleIconFiles 最后一个) + private static func appIconImage() -> UIImage { + if let icons = Bundle.main.object(forInfoDictionaryKey: "CFBundleIcons") as? [String: Any], + let primary = icons["CFBundlePrimaryIcon"] as? [String: Any], + let files = primary["CFBundleIconFiles"] as? [String], + let last = files.last, + let icon = UIImage(named: last) { + return icon + } + if let icon = UIImage(named: "AppIcon") { return icon } + let renderer = UIGraphicsImageRenderer(size: CGSize(width: 1, height: 1)) + return renderer.image { _ in } + } + // MARK: - URL / Base64 编码 /// RFC 3986 unreserved + URL-encode(与 msext line 1262-1268 等价)