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:
@@ -114,11 +114,15 @@ public final class QQShare: SharePlatform {
|
|||||||
parts.append("url=\(Self.encode(Self.base64(content.webpageUrl)))")
|
parts.append("url=\(Self.encode(Self.base64(content.webpageUrl)))")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 缩略图 —— app icon 写到 tmp 目录 → previewimageUrl = urlenc(base64("file://<path>"))
|
// 3. 缩略图 —— 字段名是 previewimagedata(图像字节 base64),不是
|
||||||
// 对齐 msext QQShareManager.m:517-522 saveImageToTempDirectory + base64
|
// msext 写的 previewimageUrl=base64("file://..."): 后者跨 app 沙盒读不到
|
||||||
if let thumbPath = Self.writeAppIconToTemp() {
|
// 本地文件(控制台 `unable to make sandbox extension` 即此原因),实测
|
||||||
let fileURL = "file://\(thumbPath)"
|
// QQ 渲染破图占位。改对齐 QQ Open SDK 的 previewimagedata 字段,把
|
||||||
parts.append("previewimageUrl=\(Self.encode(Self.base64(fileURL)))")
|
// app icon JPEG 字节本身 base64 内联到 URL。
|
||||||
|
// iOS openURL URL 长度限制 ~64KB,app 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: "&"))"
|
let urlString = "mqqapi://\(host)?\(parts.joined(separator: "&"))"
|
||||||
@@ -191,21 +195,14 @@ public final class QQShare: SharePlatform {
|
|||||||
return top
|
return top
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - 缩略图(app icon → tmp file)
|
// MARK: - 缩略图(app icon JPEG → base64 内联到 URL)
|
||||||
|
|
||||||
/// 把 app icon JPEG 写到 NSTemporaryDirectory,返回绝对路径。
|
/// app icon JPEG 0.6 → base64 字符串。QQ Open SDK previewimagedata 字段的值。
|
||||||
/// 对齐 msext QQShareManager.m:1271-1278 saveImageToTempDirectory:
|
/// 跨 app 沙盒下 file:// 路径不可读(控制台 `sandbox extension`),唯一可
|
||||||
/// 文件名 qq_share_<UUID>.jpg,JPEG 0.7 质量。失败返回 nil。
|
/// 靠的传递方式是把图像字节直接 base64 编码到 URL 参数里。
|
||||||
private static func writeAppIconToTemp() -> String? {
|
private static func appIconBase64() -> String? {
|
||||||
guard let data = appIconImage().jpegData(compressionQuality: 0.7) else { return nil }
|
guard let data = appIconImage().jpegData(compressionQuality: 0.6) else { return nil }
|
||||||
let path = NSTemporaryDirectory() + "qq_share_\(UUID().uuidString).jpg"
|
return data.base64EncodedString()
|
||||||
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() 同源策略
|
/// app icon UIImage,与 WechatShare.appIconImage() 同源策略
|
||||||
|
|||||||
Reference in New Issue
Block a user