rn-fetch-blob: Stream Closed/unexpected end of stream/ Connection reset in post request

My PC settings

  • Processor: intel core i3 8100.
  • gpu: Nvidia geforce gtx 1050 3gb.
  • 8gb ram
  • Windows 10 PRO Version 1909

Versions of installed libraries

"dependencies": {
    "@react-native-community/async-storage": "^1.11.0",
    "@react-native-community/datetimepicker": "^2.6.0",
    "@react-native-community/geolocation": "^2.0.2",
    "@react-native-community/masked-view": "^0.1.10",
    "@react-navigation/bottom-tabs": "^5.6.1",
    "@react-navigation/material-top-tabs": "^5.2.12",
    "@react-navigation/native": "^5.4.3",
    "@react-navigation/stack": "^5.4.0",
    "axios": "^0.19.2",
    "babel-plugin-root-import": "^6.5.0",
    "eslint-config-airbnb": "^18.2.0",
    "eslint-plugin-import": "^2.21.2",
    "eslint-plugin-jsx-a11y": "^6.3.0",
    "eslint-plugin-react": "^7.20.0",
    "eslint-plugin-react-hooks": "^4",
    "immer": "3.1.3",
    "indicative": "^7.4.4",
    "prop-types": "^15.7.2",
    "react": "16.11.0",
    "react-native": "0.62.2",
    "react-native-elements": "^2.0.4",
    "react-native-fs": "^2.16.6",
    "react-native-geocoding": "^0.4.0",
    "react-native-gesture-handler": "^1.6.1",
    "react-native-google-places-autocomplete": "^1.7.2",
    "react-native-hide-with-keyboard": "^1.2.1",
    "react-native-image-picker": "^2.3.3",
    "react-native-linear-gradient": "^2.5.6",
    "react-native-modal": "^11.5.6",
    "react-native-reanimated": "^1.9.0",
    "react-native-redux": "^1.0.10",
    "react-native-safe-area-context": "^2.0.3",
    "react-native-screens": "^2.8.0",
    "react-native-splash-screen": "^3.2.0",
    "react-native-tab-view": "^2.14.4",
    "react-native-vector-icons": "6.6.0",
    "react-navigation": "^4.3.9",
    "react-navigation-tabs": "^2.8.13",
    "react-redux": "7.1.0",
    "reactotron-react-native": "^5.0.0",
    "reactotron-redux": "3.1.1",
    "reactotron-redux-saga": "4.2.2",
    "redux": "4.0.4",
    "redux-persist": "^6.0.0",
    "redux-saga": "1.0.5",
    "rn-fetch-blob": "^0.12.0",
    "styled-components": "^5.1.1"
  },

android/app/src/debug/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />

<application android:usesCleartextTraffic="true" tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" />
</manifest>

android/app/src/main/AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.mobile">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />



<application
  android:name=".MainApplication"
  android:label="@string/app_name"
  android:icon="@mipmap/ic_launcher"
  android:roundIcon="@mipmap/ic_launcher_round"
  android:allowBackup="false"
  android:theme="@style/AppTheme">
  <activity
    android:name=".MainActivity"
    android:label="@string/app_name"
    android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
    android:launchMode="singleTask"
    android:windowSoftInputMode="adjustResize">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
  </activity>
  <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>
</manifest>

android/app/src/main/java/com/mobile/MainActivity.java

package com.mobile;

import com.facebook.react.ReactActivity;

import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;

import java.util.List;
import java.util.Arrays;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.react.ReactPackage;

import com.rnfs.RNFSPackage;

public class MainActivity extends ReactActivity {

  /**
   * Returns the name of the main component registered from JavaScript. This is used to schedule
   * rendering of the component.
   */
  
  @Override
  protected String getMainComponentName() {
    return "mobile";
  }

  @Override
  protected ReactActivityDelegate createReactActivityDelegate() {
    return new ReactActivityDelegate(this, getMainComponentName()) {
      @Override
      protected ReactRootView createRootView() {
       return new RNGestureHandlerEnabledRootView(MainActivity.this);
      }
    };
  }
}

android/app/src/main/java/com/mobile/MainAplication.java

package com.mobile;

import android.app.Application;
import android.content.Context;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.RNFetchBlob.RNFetchBlobPackage;
import com.RNFetchBlob.RNFetchBlobPackage;
import com.rnfs.RNFSPackage;
import com.imagepicker.ImagePickerPackage;
import com.imagepicker.ImagePickerPackage;
import com.swmansion.gesturehandler.react.RNGestureHandlerPackage;
import com.BV.LinearGradient.LinearGradientPackage;
import com.BV.LinearGradient.LinearGradientPackage;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.soloader.SoLoader;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import com.oblador.vectoricons.VectorIconsPackage;

import com.rnfs.RNFSPackage;

import java.util.List;
import java.util.Arrays;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.react.ReactPackage;

public class MainApplication extends Application implements ReactApplication {

  private final ReactNativeHost mReactNativeHost =
      new ReactNativeHost(this) {
        @Override
        public boolean getUseDeveloperSupport() {
          return BuildConfig.DEBUG;
        }

        @Override
        protected List<ReactPackage> getPackages() {
          @SuppressWarnings("UnnecessaryLocalVariable")
          List<ReactPackage> packages = new PackageList(this).getPackages();

          // Packages that cannot be autolinked yet can be added manually here, for example:
          // packages.add(new MyReactNativePackage());
          return packages;
        }

        @Override
        protected String getJSMainModuleName() {
          return "index";
        }
      };

  @Override
  public ReactNativeHost getReactNativeHost() {
    return mReactNativeHost;
  }


  @Override
  public void onCreate() {
    super.onCreate();
    SoLoader.init(this, /* native exopackage */ false);
    initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
  }

  /**
   * Loads Flipper in React Native templates. Call this in the onCreate method with something like
   * initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
   *
   * @param context
   * @param reactInstanceManager
   */
  private static void initializeFlipper(
      Context context, ReactInstanceManager reactInstanceManager) {
    if (BuildConfig.DEBUG) {
      try {
        /*
         We use reflection here to pick up the class that initializes Flipper,
        since Flipper library is not available in release mode
        */
        Class<?> aClass = Class.forName("com.mobile.ReactNativeFlipper");
        aClass
            .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
            .invoke(null, context, reactInstanceManager);
      } catch (ClassNotFoundException e) {
        e.printStackTrace();
      } catch (NoSuchMethodException e) {
        e.printStackTrace();
      } catch (IllegalAccessException e) {
        e.printStackTrace();
      } catch (InvocationTargetException e) {
        e.printStackTrace();
      }
    }
  }
}

Code

  • Backend: i’m using multer store locally the image, it works properly.
import multer from 'multer';
import crypto from 'crypto';
import { extname, resolve } from 'path';

export default  {
    storage: multer.diskStorage({ 
        destination: resolve(__dirname, '..', '..', 'temp', 'uploads' ),
        filename: ( req, file, cb ) => {
            crypto.randomBytes(8, ( err, res ) => {
                if ( err ) return cb(err);

                return cb( null, res.toString( 'hex' ) + extname(file.originalname));
            } )
        },
    })
};
  • In routes.js I read the file uploaded at the client.
routes.post('/files', userAuth , upload.single('file'), FileController.store);
  • in the FileController I store the path and the name in my database, working either.
 async store( req, res ) {
       const { filename: path, originalname: name } =  req.file;
                

        const file = await File.create({
            name,
            path
        })
        
        const user = await User.update( {avatar_id: file.toJSON().id}, {where: {id: req.userId}} );


        return res.json(file);
    }

React Native Code

I have a function to store the image.

const storeImage = async (response) => {
    console.log(response);

    RNFetchBlob.config({
      trusty: true,
      timeout: 1000,
    })
      .fetch(
        'POST',
        url,
        {
          'Content-Type': 'multipart/form-data',
          authorization: `Bearer ${token}`,
        },
        [
          {
            name: 'file',
            filename: response.fileName,
            type: response.type,
            data: RNFetchBlob.wrap(response.uri),
          },
        ]
      )
      .then((res) => {
        console.tron.log(res);
      })
      .catch((err) => {
        console.tron.log(err);
      });
  };

And I have a function to call the image picker, and the function to send the image

const showImagePicker = () => {
      console.log(avatarSource);
      console.log(oldAvatarSource);

      ImagePicker.showImagePicker(options, (response) => {
        if (response.didCancel) {
          return null;
        }
        if (response.error) {
          Alert.alert(
            'Erro!',
            'Não consegui recuperar a imagem, tente novamente!'
          );
        } else {
          const source = { uri: response.uri };
          console.log(response); //returns the correct object, with all information
          setAvatarProperties(response);
          setAvatarSource(source);

          /*= ===============SEND IMAGE TO BACKEND================== */
         storeImage (response);
          
        }
      });
    }

What I'm facing

I'm making POST request with RNFetchBlob to my API in backend with nodejs, sometimes it works, sometimes it don't. Sometimes when I upload the image, it is sent to backend fine. But most of the times if I repeat this process without reloading the app, the backend returns me the error:
(node:14568) UnhandledPromiseRejectionWarning: TypeError: Cannot destructure property 'filename' of '(intermediate value)' as it is undefined.
    at update (C:\Users\roble\Documents\programming\apps\embaixada-do-bem-api\src\app\controllers\FileController.js:41:27)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
(node:14568) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:14568) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. 
And the image is not read by backend until I reload the app and make the request again.

And at the browser(debug mode), I receive the Message some seconds after:

Error: unexpected end of stream
    at VM4 index.bundle:154683
    at MessageQueue.__invokeCallback (VM4 index.bundle:5652)
    at VM4 index.bundle:5356
    at MessageQueue.__guard (VM4 index.bundle:5564)
    at MessageQueue.invokeCallbackAndReturnFlushedQueue (VM4 index.bundle:5355)
    at VM3 debuggerWorker.cff11639.js:4

And in my phone, I receive the messages:

WhatsApp Image 2020-07-19 at 15 45 10

WhatsApp Image 2020-07-19 at 16 06 39

How can I solve that?

I already seen the issue

unexpected end of stream (#419)
and
unexpected end of stream (#399) 
. Did everything i found on the web to solve it, but nothing could.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 1
  • Comments: 16

Most upvoted comments

@trsp400 hi, did you solve the unexpected end of stream error?

Yes just upgrade you FLIPPER_VERSION=0.52.1 under android/app/gradlew.properties

Suddenly started facing the same problem in FLIPPER_VERSION=0.125.0. Event updating to FLIPPER_VERSION=0.182.0 is not helping

in my case, it is caused by another HTTP interceptor running at the same time (i use chucker)