You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

火山引擎 最新活动