Expo React Native实现iOS端照片自动后台上传至Firebase的问题求助
Expo React Native实现iOS端照片自动后台上传至Firebase的问题求助
我正在开发一款Expo React Native应用(目前先针对iOS),需求是让用户用设备相机拍照后,照片能自动上传到Firebase的指定事件中,优先支持后台上传,如果做不到的话,也可以把上传任务加入队列,等用户下次打开应用到前台时再执行。
我已经试了几种不同的实现方式:
- 利用MediaLibrary的更新回调,把新增图片的URI存入AsyncStorage,然后用一个定时运行的上传器从这个列表取文件上传,同时用另一个已上传列表在AsyncStorage里跟踪进度
- 试过几个任务队列库,但都没成功(本来觉得这个是最佳方案:监听器创建上传任务,然后上传器逐个处理)
- 自己写了一个简易的上传器(代码在下面)
目前这些方法都能成功把图片上传到指定事件,但一次性拍太多照片时应用会崩溃,而且在后台运行时可靠性也不高。
我是React Native新手,非常希望能得到大家的建议和帮助!另外我用了expo-router,把这段上传相关的代码放在了_layout里,只要isLive触发就会在应用所有页面运行,这种做法是否合适?如果需要更多细节我可以补充。
下面是我第三次尝试写的简易上传器代码:
import React, { createContext, useState, useEffect, useRef } from "react"; import { Slot } from "expo-router"; import { useFonts } from "expo-font"; import { Montserrat_400Regular, Montserrat_400Regular_Italic, Montserrat_600SemiBold, } from "@expo-google-fonts/montserrat"; import * as MediaLibrary from "expo-media-library"; import AsyncStorage from "@react-native-async-storage/async-storage"; import uploadImageToEvents from "../services/uploadImageToEvents"; import EventListeners from "../services/EventListeners"; export const AppContext = createContext(null); function FontLoader({ children }) { const [loadedFonts] = useFonts({ MontserratRegular: Montserrat_400Regular, MontserratItalic: Montserrat_400Regular_Italic, MontserratSemiBold: Montserrat_600SemiBold, }); if (!loadedFonts) { return null; } return children; } export default function HomeLayout() { const [userDetails, setUserDetails] = useState<UserDetails>( {} as UserDetails ); const [userEvents, setUserEvents] = useState<UserEvents>({} as UserEvents); const [selectedEvent, setSelectedEvent] = useState({}); const [homeTabState, setHomeTabState] = useState<HomeTabState>("memories"); const [isLive, setIsLive] = useState(false); const [liveEventIds, setLiveEventIds] = useState([]); const uploadQueue = useRef([]); const [imagesToUpload, setImagesToUpload] = useState(false); useEffect(() => { setImagesToUpload(uploadQueue.current.length > 0); }, [imagesToUpload]); useEffect(() => { if (!imagesToUpload || liveEventIds.length === 0) { return; } const uploadImages = async () => { const photoUris = uploadQueue.current.shift(); try { await uploadImageToEvents(photoUris, liveEventIds); setImagesToUpload(false); } catch (err) { console.log(err); } }; uploadImages(); }, [imagesToUpload]); const updatePhotos = async (insertedAssets: MediaLibrary.Asset[]) => { try { const newUris = insertedAssets.map((asset) => asset.uri); uploadQueue.current.push(newUris); setImagesToUpload(true); } catch (err) { alert(err); } }; useEffect(() => { let newSubscription: MediaLibrary.Subscription | null = null; (async () => { try { if (isLive) { const { status } = await MediaLibrary.requestPermissionsAsync(); if (status !== "granted") { alert("User needs to grant permission to photos."); return; } newSubscription = MediaLibrary.addListener( async (event: MediaLibrary.MediaLibraryAssetsChangeEvent) => { await updatePhotos(event.insertedAssets); } ); } else { newSubscription?.remove(); } } catch (err) { alert(err); } })(); return () => { newSubscription?.remove(); }; }, [isLive]); return ( <FontLoader> <AppContext.Provider value={{ userDetails, setUserDetails, userEvents, setUserEvents, selectedEvent, setSelectedEvent, homeTabState, setHomeTabState, }} > <EventListeners events={userEvents} setIsLive={setIsLive} setLiveEventIds={setLiveEventIds} /> <Slot /> </AppContext.Provider> </FontLoader> ); }
备注:内容来源于stack exchange,提问作者James Heavey




