Vydia / react-native-background-upload

Upload files in your React Native app even while it's backgrounded. Supports Android and iOS, including camera roll assets.
MIT License
720 stars 325 forks source link

Upload Randomly Fails on Android Devices and Error Throws Undefined error.data #303

Open trvictorvicera opened 2 years ago

trvictorvicera commented 2 years ago

Hello, we have been having this issue for a while where we have android users who experience upload failures. This happens on random devices using android 8 to 11. It doesn't occur in iOS.

We track these events (e.g. upload failures) on MixPanel and in this case, we hooked it up on the catch block of the upload. The problem is we are getting undefined error.data on this particular case, which doesn't really gives us any useful info for debugging. We also can't reproduce this issue as well.

Dependencies:

"dependencies": {
    "@babel/core": "^7.17.8",
    "@babel/runtime": "^7.17.8",
    "@log4js-node/log4js-api": "^1.0.2",
    "@react-native-async-storage/async-storage": "^1.15.5",
    "@react-native-community/masked-view": "^0.1.11",
    "@react-native-community/netinfo": "^8.2.0",
    "@react-navigation/native": "^6.0.0",
    "@react-navigation/stack": "^6.0.0",
    "@redux-offline/redux-offline": "2.6.0-native.1",
    "@segment/analytics-react-native": "^1.5.2",
    "@segment/analytics-react-native-firebase": "^1.5.0",
    "@wdio/reporter": "^7.19.1",
    "axios": "^0.21.1",
    "babel-eslint": "^10.1.0",
    "react": "17.0.2",
    "react-native": "0.67.4",
    "react-native-app-settings": "^2.0.1",
    "react-native-background-upload": "^6.5.0",
    "react-native-device-info": "^8.1.7",
    "react-native-fs": "^2.18.0",
    "react-native-geolocation-service": "^5.3.0-beta.3",
    "react-native-gesture-handler": "^1.10.3",
    "react-native-get-random-values": "^1.7.0",
    "react-native-image-pan-zoom": "^2.1.12",
    "react-native-image-picker": "^4.0.6",
    "react-native-keyboard-aware-scroll-view": "^0.9.4",
    "react-native-permissions": "^3.0.5",
    "react-native-reanimated": "^2.5.0",
    "react-native-safe-area-context": "^3.2.0",
    "react-native-screens": "^3.4.0",
    "react-native-sha256": "^1.4.7",
    "react-native-splash-screen": "^3.2.0",
    "react-native-svg": "^12.1.1",
    "react-native-vector-icons": "^8.1.0",
    "react-redux": "^7.2.4",
    "redux": "^4.1.1",
    "redux-devtools-extension": "^2.13.9",
    "redux-logger": "^3.0.6",
    "redux-thunk": "^2.3.0",
    "uuid": "^8.3.2",
    "wdio-html-nice-reporter": "^8.0.0"
},
// this will send off all photos that are not yet uploaded to react-native-background-upload in parallel, then await
// all of them
const promises = Object.entries(photos)
    .reduce((acc, [room, roomPhotos]) => ([...acc, ...roomPhotos.map((photo) => ({
        room,
        photo
    }))]), [])
    .filter(({
        photo
    }) => !uploaded[photo.uri])
    .map(({
        room,
        photo
    }) => dispatch(uploadPhoto(saved[room], photo, total, picProgress, count)));
try {
    await Promise.all(promises);
} catch (e) {
    // one of the uploads failed, do not complete
    trackEventsViaSegment('Upload Failed', 'Upload', config.APP_VERSION, {
        Reason: `${JSON.stringify(e)}; data: ${String(e.data).substring(0, 200)}`,
        Type: workflowType,
        Category: category
    });
    return;
}
const uploadPhoto = (roomId, photo, total, picProgress, picCount) => async (dispatch, getState) => {
    const {
        code: { code, apiToken },
        env: { config },
    } = getState();
    const url = `hehe`;
    try {
        const path = Platform.OS === 'android' ? photo.uri.replace('file://', '') : photo.uri;
        const uploadId = await Upload.startUpload({
            type: 'multipart',
            method: 'POST',
            url,
            path,
            field: 'photo',
            headers: { 'X-API-Token': apiToken },
            parameters: {
                latitude: `${photo.latitude}`,
                longitude: `${photo.longitude}`,
                taken: photo.taken,
                hashCode: photo.hashCode
            },
            notification: {
                enabled: true,
                autoClear: true
            },
            writeTimeout: 240
        });
        Upload.addListener('progress', uploadId, ({ progress: p }) => {
            const progress = getState().photos.progress;
            dispatch({
                type: UPLOAD_PROGRESS,
                payload: { progress: progress + (p * photo.fileSize) / total,
                            oneFileSize: formatBytes(photo.fileSize),
                            totalFileSize: total,
                            photoProgress: picProgress,
                            photosCount: picCount,
                            photoId: photo.uuid},
                source: 'progress-event'
            });
        });
        await new Promise((success, failure) => {
            Upload.addListener('error', uploadId, failure);
            Upload.addListener('cancelled', uploadId, failure);
            Upload.addListener('completed', uploadId, success);
        });
    } catch (e) {
        dispatch({
            type: UPLOAD_ERROR,
            payload: {
                error: e,
                message: `${e}`,
                url
            }
        });
        throw e;
    }
    dispatch({
        type: UPLOADED,
        payload: {
            imageUri: photo.uri,
            progress: getState().photos.progress + photo.fileSize / total
        }
    });
};

Can somebody please point me to the right direction? Thank you!