React Native中expo-av缓存视频无法播放的问题求助
Expo AV 本地缓存视频无法播放问题排查与解决
问题场景
在裸React Native项目中集成expo-av后,尝试用react-native-fs缓存视频并播放本地文件,已获取到合法的本地路径(如/Users/jamess/Library/Developer/CoreSimulator/.../Documents/video5_g243f4.mp4),但expo-av的Video组件无法播放该缓存视频,而react-native-video可正常运行。相关实现代码如下:
const App = (props: Props) => { const [video, setVideo] = useState(''); useEffect(() => { downloadVideo(props.video); }, []); const downloadVideo = async (videoUrl) => { const filename = videoUrl.substring(videoUrl.lastIndexOf('/') + 1); const cachePath = RNFS.DocumentDirectoryPath const exists = await RNFS.exists(cachePath + filename); if (!exists) { const data = await fetch(videoUrl); const blob = await data.blob(); await RNFS.writeFile(cachePath + filename, blob); } console.log(`${cachePath}/${filename}`) setVideo(`${cachePath}/${filename}`) }; console.log(props.video) return ( <View> <Video source={{uri: video}} resizeMode="cover" repeat shouldPlay isMuted={false} style={styles.backgroundVideo} /> </View> ); } export default App;
问题原因
- 文件写入逻辑错误:RNFS的
writeFile方法默认仅支持字符串或base64编码数据,直接传入fetch获取的Blob对象会导致文件内容损坏。react-native-video可能对这类损坏文件有兼容处理,但expo-av的解码器要求文件必须完全符合格式规范。 - 路径拼接隐患:代码中
cachePath + filename的写法可能出现路径分隔符缺失(比如DocumentDirectoryPath结尾无/时,拼接后会变成/xxxDocumentsvideo.mp4),导致实际写入路径与播放路径不一致。
解决方案
方案1:改用RNFS.downloadFile下载视频(推荐)
直接使用RNFS的原生下载API,避免手动处理Blob,确保文件完整性:
const downloadVideo = async (videoUrl) => { const filename = videoUrl.substring(videoUrl.lastIndexOf('/') + 1); const cachePath = `${RNFS.DocumentDirectoryPath}/${filename}`; const exists = await RNFS.exists(cachePath); if (!exists) { const downloadResult = await RNFS.downloadFile({ fromUrl: videoUrl, toFile: cachePath }).promise; // 校验下载状态 if (downloadResult.statusCode !== 200) { console.error('视频下载失败'); return; } } setVideo(cachePath); };
方案2:正确处理Blob写入(不推荐,易出问题)
若必须用fetch获取Blob,需先将其转换为base64编码再写入:
const downloadVideo = async (videoUrl) => { const filename = videoUrl.substring(videoUrl.lastIndexOf('/') + 1); const cachePath = `${RNFS.DocumentDirectoryPath}/${filename}`; const exists = await RNFS.exists(cachePath); if (!exists) { const response = await fetch(videoUrl); const blob = await response.blob(); // 将Blob转换为base64 const base64Data = await new Promise((resolve) => { const reader = new FileReader(); reader.onloadend = () => resolve(reader.result.split(',')[1]); reader.readAsDataURL(blob); }); await RNFS.writeFile(cachePath, base64Data, 'base64'); } setVideo(cachePath); };
额外检查项
- 确认
styles.backgroundVideo设置了明确的宽高,否则视频可能因尺寸为0而不可见 - 可在播放前读取文件大小,验证文件是否完整写入:
const fileInfo = await RNFS.stat(cachePath); console.log('文件大小:', fileInfo.size);
内容的提问来源于stack exchange,提问作者James
相关产品推荐
相关产品推荐

