Expo环境下React Native WebView如何实现图片上传功能
Expo 全WebView架构唤起图片选择器实现方案
不需要eject成裸RN工程,也不需要适配RN CLI文档里的原生修改逻辑,直接用Expo生态官方依赖+WebView双向通信就能实现,全版本Expo SDK通用,Expo Go调试环境直接可用。
依赖安装与基础配置
安装Expo官方维护的无原生link依赖:
npx expo install expo-image-picker react-native-webview
在项目根目录app.json中补充必要权限声明:
{ "expo": { "ios": { "infoPlist": { "NSPhotoLibraryUsageDescription": "访问相册用于选择需要上传的图片" } }, "android": { "permissions": ["READ_MEDIA_IMAGES", "READ_EXTERNAL_STORAGE"] } } }
RN容器层逻辑实现
核心逻辑是监听WebView发来的唤起选择器指令,调用Expo图片选择器拿到结果后回传给WebView,代码示例:
import { View } from 'react-native'; import { WebView } from 'react-native-webview'; import * as ImagePicker from 'expo-image-picker'; import { useRef } from 'react'; export default function WebViewContainer() { const webViewRef = useRef(null); const handleWebViewMessage = async (event) => { try { const message = JSON.parse(event.nativeEvent.data); if (message.type !== 'TRIGGER_IMAGE_PICKER') return; // 申请相册权限 const permissionResult = await ImagePicker.requestMediaLibraryPermissionsAsync(); if (!permissionResult.granted) { webViewRef.current.postMessage(JSON.stringify({ type: 'IMAGE_PICKER_RESPONSE', success: false, msg: '相册权限未开启' })); return; } // 唤起图片选择器,可按需配置裁剪、质量、多选等参数 const pickerResult = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images, allowsEditing: true, quality: 0.8, base64: true, }); if (pickerResult.canceled) return; const selectedAsset = pickerResult.assets[0]; // 回传选中的图片数据给WebView webViewRef.current.postMessage(JSON.stringify({ type: 'IMAGE_PICKER_RESPONSE', success: true, data: { uri: selectedAsset.uri, base64: selectedAsset.base64, width: selectedAsset.width, height: selectedAsset.height } })); } catch (err) { webViewRef.current?.postMessage(JSON.stringify({ type: 'IMAGE_PICKER_RESPONSE', success: false, msg: '选择图片失败' })); } }; return ( <View style={{ flex: 1 }}> <WebView ref={webViewRef} source={{ uri: '你的Web页面地址' }} onMessage={handleWebViewMessage} originWhitelist={['*']} javaScriptEnabled={true} /> </View> ); }
Web页面端适配
网页端无需引入额外依赖,直接通过react-native-webview注入的全局对象通信即可:
<button id="selectImgBtn">选择图片</button> <div id="imgWrap"></div> <script> // 监听RN层回传的图片选择结果 window.addEventListener('message', (e) => { const res = JSON.parse(e.data); if (res.type !== 'IMAGE_PICKER_RESPONSE') return; if (!res.success) { alert(res.msg); return; } // 渲染选中的图片,可按需替换为上传逻辑 const img = document.createElement('img'); img.src = `data:image/jpeg;base64,${res.data.base64}`; img.style.maxWidth = '100%'; document.getElementById('imgWrap').appendChild(img); }); // 按钮点击向RN层发送唤起选择器的指令 document.getElementById('selectImgBtn').addEventListener('click', () => { window.ReactNativeWebView.postMessage(JSON.stringify({ type: 'TRIGGER_IMAGE_PICKER' })); }); </script>
避坑说明
- 不需要按照react-native-webview官方文档修改安卓/iOS原生文件开启文件上传支持,那套逻辑仅适用于裸RN CLI工程,Expo下使用需要额外编写config插件修改原生配置,稳定性远低于双向通信方案,且无法自定义选择器逻辑。
- 大尺寸图片不建议通过base64传输,容易触发WebView消息体积限制导致通信失败,可将图片本地URI转换为Blob对象后再传入WebView使用。
- 如果需要支持拍照唤起,替换
launchImageLibraryAsync为launchCameraAsync,在app.json中补充相机权限声明即可,逻辑完全一致。 - Expo SDK 48及以上版本默认已开启WebView的JavaScript执行、跨域通信相关配置,不需要额外设置属性。
内容的提问来源于stack exchange,提问作者Intaek
相关产品推荐
相关产品推荐

