wx.openDocument「打开文件失败」:带查询参数的 OSS 链接吃掉了扩展名
在某个 C 端小程序里,用户点击「查看合同」按钮,页面先转圈——然后弹出「打开文件失败」。后台日志显示 wx.downloadFile 正常返回 statusCode: 200,文件字节数也对得上,按道理 PDF 应该能打开。但就是打不开。
更诡异的是:在微信开发者工具里跑完全没问题,换成真机就必现。这种"工具好、真机坏"的组合,排查起来格外消耗心力。
现象
wx.downloadFile({
url: 'https://oss.example.com/contract.pdf?OSSAccessKeyId=xxx&Expires=1742025600&Signature=yyy',
success(res) {
// res.statusCode === 200,下载成功
// res.tempFilePath === "http://tmp/wx_file_abc123" ← 注意:没有扩展名
wx.openDocument({
filePath: res.tempFilePath,
// 没有传 fileType
});
// 真机上:触发 fail 回调,toast "打开文件失败"
}
});下载成功,tempFilePath 有值,但 wx.openDocument 进了 fail 分支。
根因
wx.downloadFile 生成临时文件路径的规则是:用随机 ID 命名,不保留原始文件扩展名。
当源 URL 是干净路径(如 https://cdn.example.com/file.pdf)时,某些版本的微信基础库会从 URL 路径中提取 .pdf 写入临时文件名,侥幸能用。但一旦 URL 带有查询参数(OSS 预签名 URL、CDN token 鉴权 URL 等),这个"顺手提取"的逻辑就会失效,临时文件落盘后没有任何扩展名。
wx.openDocument 不做文件头 Magic Bytes 检测,完全依赖扩展名判断文件类型。扩展名缺失 → 类型未知 → 静默失败。
开发者工具之所以有时能正常打开,是因为 DevTools 的文件处理逻辑与真机基础库存在差异,这让 bug 在开发阶段完全隐身。
wx.downloadFile 生成的 tempFilePath 长这样:
http://tmp/wx_file_abc123 ← 无扩展名
wx.openDocument 拿到这个路径后无法确定文件类型,在真机上必然报错。微信开发者工具的文件处理与真机不一致,导致此 bug 在开发阶段难以发现。
正解
从原始 URL 里主动提取文件类型,通过 fileType 参数显式传给 wx.openDocument:
// 从 URL 中提取文件类型(先去掉查询参数再取扩展名)
function getFileType(url) {
if (!url) return '';
const path = url.split('?')[0];
const ext = path.split('.').pop().toLowerCase();
const supported = ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf'];
return supported.includes(ext) ? ext : '';
}
const fileUrl = 'https://oss.example.com/contract.pdf?OSSAccessKeyId=xxx&Expires=1742025600';
const fileType = getFileType(fileUrl); // → 'pdf'
wx.downloadFile({
url: fileUrl,
success(res) {
if (res.statusCode !== 200) return;
const opts = {
filePath: res.tempFilePath,
showMenu: true,
fail() {
wx.showToast({ title: '打开文件失败', icon: 'none' });
}
};
if (fileType) opts.fileType = fileType; // 关键:显式传类型
wx.openDocument(opts);
}
});fileType 是 wx.openDocument 的可选但关键参数。当 tempFilePath 无扩展名时,不传就必崩。
解析逻辑:url.split('?')[0] 先剥离查询参数,再 .split('.').pop() 取扩展名,最后做白名单校验(wx.openDocument 支持:doc / docx / xls / xlsx / ppt / pptx / pdf)。
延伸
- 如果文件 URL 完全没有扩展名(比如后端返回
/api/download/123这种流式接口),前端无法从 URL 推断类型,需要后端在 API 响应里额外提供fileType字段。 - 图片文件(jpg / png 等)不要用
wx.openDocument,走wx.previewImage。 - 同样受影响:
wx.saveFile——无扩展名的临时文件保存后,设备上的文件管理器也可能无法识别文件类型。
一句话外卖
调用
wx.openDocument时,永远从原始 URL 解析fileType显式传参——不要依赖微信基础库从tempFilePath反推,尤其是 OSS 预签名 URL 这类带查询参数的场景。