4.B QQ 链接分享改 previewimagedata 内联 base64:修缩略图破图

实测控制台 `unable to make sandbox extension: [1: Operation not permitted]`
证实 previewimageUrl=file://... 跨 app 沙盒无效 —— QQ 进程读不到调用方 app
tmp 目录里的文件,渲染破图占位。

msext 这段实现一直是装饰性代码(线上同样破图但没人改)。新外壳按原则 B
现代化修正:

- 字段名改用 QQ Open SDK 实际约定的 `previewimagedata`(值=图像字节
  base64,而非 file path base64)
- 内容来源改用 app icon JPEG 0.6 编码后 base64 内联到 URL
- 删 writeAppIconToTemp(不再写临时文件)

App icon 60-180px JPEG 0.6 后 base64 约 5-15KB,iOS openURL ~64KB 限额下
余量充足。这条修正违反"原则 A 等价 msext"吗?不——契约 §3.1[2]
sharesuccess 反向 callback 已经在 commit 9bd5a81 落地,与 H5 唯一可见
的契约面(callback 时机与载荷)保持等价。msext 这处缩略图字段错写属于
原项目内部 bug,新外壳的"原则 B 内部自由重构"覆盖范围,按现代标准修正
不破契约。
This commit is contained in:
joywayer
2026-06-24 18:45:05 +08:00
parent 2f3a35892b
commit 1d231a52ef
+16 -19
View File
@@ -114,11 +114,15 @@ public final class QQShare: SharePlatform {
parts.append("url=\(Self.encode(Self.base64(content.webpageUrl)))")
}
// 3. app icon tmp previewimageUrl = urlenc(base64("file://<path>"))
// msext QQShareManager.m:517-522 saveImageToTempDirectory + base64
if let thumbPath = Self.writeAppIconToTemp() {
let fileURL = "file://\(thumbPath)"
parts.append("previewimageUrl=\(Self.encode(Self.base64(fileURL)))")
// 3. previewimagedata base64
// msext previewimageUrl=base64("file://..."): app
// `unable to make sandbox extension`
// QQ QQ Open SDK previewimagedata
// app icon JPEG base64 URL
// iOS openURL URL ~64KBapp icon 60-180px JPEG 0.6
// base64 5-15KB
if let thumbBase64 = Self.appIconBase64() {
parts.append("previewimagedata=\(Self.encode(thumbBase64))")
}
let urlString = "mqqapi://\(host)?\(parts.joined(separator: "&"))"
@@ -191,21 +195,14 @@ public final class QQShare: SharePlatform {
return top
}
// MARK: - app icon tmp file
// MARK: - app icon JPEG base64 URL
/// app icon JPEG NSTemporaryDirectory
/// msext QQShareManager.m:1271-1278 saveImageToTempDirectory:
/// qq_share_<UUID>.jpgJPEG 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 JPEG 0.6 base64 QQ Open SDK previewimagedata
/// app file:// `sandbox extension`
/// base64 URL
private static func appIconBase64() -> String? {
guard let data = appIconImage().jpegData(compressionQuality: 0.6) else { return nil }
return data.base64EncodedString()
}
/// app icon UIImage WechatShare.appIconImage()