Expo React Native从API获取PDF并在设备默认浏览器显示的问题
解决方案(无需eject Expo)
核心思路:手动修正API返回的Blob类型,将其转换为PDF格式后保存到本地,再通过Expo提供的可共享URI在默认浏览器中打开。
步骤1:正确获取并转换API响应
API返回的是PDF数据但Content-Type被错误标记为application/json,需要手动将响应Blob转换为PDF类型:
import * as FileSystem from 'expo-file-system'; import * as Linking from 'expo-linking'; const fetchAndOpenPDF = async () => { try { const res = await fetch('API_URL', { method: 'GET', headers: { Authorization: 'AUTH_TOKEN', // GET请求无需设置content-type,服务器返回的是PDF二进制数据 }, }); if (!res.ok) throw new Error('API请求失败'); // 将响应转为Blob,并手动指定正确的PDF MIME类型 const rawBlob = await res.blob(); const pdfBlob = new Blob([rawBlob], { type: 'application/pdf' }); // 步骤2:将Blob转为Base64并保存到本地 const reader = new FileReader(); reader.readAsDataURL(pdfBlob); reader.onloadend = async () => { // 去掉Data URL前缀,只保留Base64数据 const base64Data = reader.result.split(',')[1]; // 生成缓存目录下的PDF文件路径 const fileUri = `${FileSystem.cacheDirectory}temp.pdf`; // 写入Base64数据到文件 await FileSystem.writeAsStringAsync(fileUri, base64Data, { encoding: FileSystem.EncodingType.Base64, }); // 步骤3:获取可被外部应用访问的Content URI // iOS沙盒限制,直接用file://路径无法被浏览器访问 const contentUri = await FileSystem.getContentUriAsync(fileUri); // 在默认浏览器中打开PDF await Linking.openURL(contentUri); // 可选:用完后删除临时文件,节省存储空间 setTimeout(() => { FileSystem.deleteAsync(fileUri).catch(err => console.warn('删除临时文件失败:', err)); }, 5000); }; } catch (err) { console.error('处理PDF失败:', err); } };
错误原因分析
- FileSystem.downloadAsync失败:该方法无法自定义请求头(如Authorization),因此无法通过API的权限校验,必须用
fetch手动获取数据。 - 直接打开file://路径报错:iOS沙盒机制禁止外部应用(如浏览器)直接访问App私有目录的file路径,必须使用
getContentUriAsync生成的content://协议URI。 - writeAsStringAsync崩溃:直接写入Blob的原始字符串会导致编码错误,必须将Blob转换为Base64格式后再写入。
注意事项
- 确保已安装依赖:
expo install expo-file-system expo-linking - 必须在真机上测试,模拟器的文件系统权限和行为与真机存在差异
- 临时文件建议在使用后删除,避免占用设备存储空间
内容的提问来源于stack exchange,提问作者jlewis
相关产品推荐
相关产品推荐

