Add blob implementation with WebSocket integration

Summary:
This is the first PR from a series of PRs grabbou and me will make to add blob support to React Native. The next PR will include blob support for XMLHttpRequest.

I'd like to get this merged with minimal changes to preserve the attribution. My next PR can contain bigger changes.

Blobs are used to transfer binary data between server and client. Currently React Native lacks a way to deal with binary data. The only thing that comes close is uploading files through a URI.

Current workarounds to transfer binary data includes encoding and decoding them to base64 and and transferring them as string, which is not ideal, since it increases the payload size and the whole payload needs to be sent via the bridge every time changes are made.

The PR adds a way to deal with blobs via a new native module. The blob is constructed on the native side and the data never needs to pass through the bridge. Currently the only way to create a blob is to receive a blob from the server via websocket.

The PR is largely a direct port of https://github.com/silklabs/silk/tree/master/react-native-blobs by philikon into RN (with changes to integrate with RN), and attributed as such.

> **Note:** This is a breaking change for all people running iOS without CocoaPods. You will have to manually add `RCTBlob.xcodeproj` to your `Libraries` and then, add it to Build Phases. Just follow the process of manual linking. We'll also need to document this process in the release notes.

Related discussion - https://github.com/facebook/react-native/issues/11103

- `Image` can't show image when `URL.createObjectURL` is used with large images on Android

The websocket integration can be tested via a simple server,

```js
const fs = require('fs');
const http = require('http');

const WebSocketServer = require('ws').Server;

const wss = new WebSocketServer({
  server: http.createServer().listen(7232),
});

wss.on('connection', (ws) => {
  ws.on('message', (d) => {
    console.log(d);
  });

  ws.send(fs.readFileSync('./some-file'));
});
```

Then on the client,

```js
var ws = new WebSocket('ws://localhost:7232');

ws.binaryType = 'blob';

ws.onerror = (error) => {
  console.error(error);
};

ws.onmessage = (e) => {
  console.log(e.data);
  ws.send(e.data);
};
```

cc brentvatne ide
Closes https://github.com/facebook/react-native/pull/11417

Reviewed By: sahrens

Differential Revision: D5188484

Pulled By: javache

fbshipit-source-id: 6afcbc4d19aa7a27b0dc9d52701ba400e7d7e98f
This commit is contained in:
Philipp von Weitershausen
2017-07-26 08:23:20 -07:00
committed by Facebook Github Bot
parent 91493f6b9d
commit ed903099b4
26 changed files with 1800 additions and 307 deletions
@@ -13,7 +13,6 @@ import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import com.facebook.react.LazyReactPackage;
import com.facebook.react.animated.NativeAnimatedModule;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.ModuleSpec;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
@@ -30,6 +29,7 @@ import com.facebook.react.flat.RCTVirtualTextManager;
import com.facebook.react.module.model.ReactModuleInfoProvider;
import com.facebook.react.modules.accessibilityinfo.AccessibilityInfoModule;
import com.facebook.react.modules.appstate.AppStateModule;
import com.facebook.react.modules.blob.BlobModule;
import com.facebook.react.modules.camera.CameraRollManager;
import com.facebook.react.modules.camera.ImageEditingManager;
import com.facebook.react.modules.camera.ImageStoreManager;
@@ -74,12 +74,10 @@ import com.facebook.react.views.toolbar.ReactToolbarManager;
import com.facebook.react.views.view.ReactViewManager;
import com.facebook.react.views.viewpager.ReactViewPagerManager;
import com.facebook.react.views.webview.ReactWebViewManager;
import javax.inject.Provider;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.inject.Provider;
/**
* Package defining basic modules and view managers.
@@ -101,150 +99,207 @@ public class MainReactPackage extends LazyReactPackage {
@Override
public List<ModuleSpec> getNativeModules(final ReactApplicationContext context) {
return Arrays.asList(
new ModuleSpec(AccessibilityInfoModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new AccessibilityInfoModule(context);
}
}),
new ModuleSpec(AppStateModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new AppStateModule(context);
}
}),
new ModuleSpec(AsyncStorageModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new AsyncStorageModule(context);
}
}),
new ModuleSpec(CameraRollManager.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new CameraRollManager(context);
}
}),
new ModuleSpec(ClipboardModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ClipboardModule(context);
}
}),
new ModuleSpec(DatePickerDialogModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new DatePickerDialogModule(context);
}
}),
new ModuleSpec(DialogModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new DialogModule(context);
}
}),
new ModuleSpec(FrescoModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new FrescoModule(context, true, mConfig != null ? mConfig.getFrescoConfig() : null);
}
}),
new ModuleSpec(I18nManagerModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new I18nManagerModule(context);
}
}),
new ModuleSpec(ImageEditingManager.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ImageEditingManager(context);
}
}),
new ModuleSpec(ImageLoaderModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ImageLoaderModule(context);
}
}),
new ModuleSpec(ImageStoreManager.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ImageStoreManager(context);
}
}),
new ModuleSpec(IntentModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new IntentModule(context);
}
}),
new ModuleSpec(LocationModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new LocationModule(context);
}
}),
new ModuleSpec(NativeAnimatedModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new NativeAnimatedModule(context);
}
}),
new ModuleSpec(NetworkingModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new NetworkingModule(context);
}
}),
new ModuleSpec(NetInfoModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new NetInfoModule(context);
}
}),
new ModuleSpec(PermissionsModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new PermissionsModule(context);
}
}),
new ModuleSpec(ShareModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ShareModule(context);
}
}),
new ModuleSpec(StatusBarModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new StatusBarModule(context);
}
}),
new ModuleSpec(TimePickerDialogModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new TimePickerDialogModule(context);
}
}),
new ModuleSpec(ToastModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ToastModule(context);
}
}),
new ModuleSpec(VibrationModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new VibrationModule(context);
}
}),
new ModuleSpec(WebSocketModule.class, new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new WebSocketModule(context);
}
}));
new ModuleSpec(
AccessibilityInfoModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new AccessibilityInfoModule(context);
}
}),
new ModuleSpec(
AppStateModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new AppStateModule(context);
}
}),
new ModuleSpec(
BlobModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new BlobModule(context);
}
}),
new ModuleSpec(
AsyncStorageModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new AsyncStorageModule(context);
}
}),
new ModuleSpec(
CameraRollManager.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new CameraRollManager(context);
}
}),
new ModuleSpec(
ClipboardModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ClipboardModule(context);
}
}),
new ModuleSpec(
DatePickerDialogModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new DatePickerDialogModule(context);
}
}),
new ModuleSpec(
DialogModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new DialogModule(context);
}
}),
new ModuleSpec(
FrescoModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new FrescoModule(
context, true, mConfig != null ? mConfig.getFrescoConfig() : null);
}
}),
new ModuleSpec(
I18nManagerModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new I18nManagerModule(context);
}
}),
new ModuleSpec(
ImageEditingManager.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ImageEditingManager(context);
}
}),
new ModuleSpec(
ImageLoaderModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ImageLoaderModule(context);
}
}),
new ModuleSpec(
ImageStoreManager.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ImageStoreManager(context);
}
}),
new ModuleSpec(
IntentModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new IntentModule(context);
}
}),
new ModuleSpec(
LocationModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new LocationModule(context);
}
}),
new ModuleSpec(
NativeAnimatedModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new NativeAnimatedModule(context);
}
}),
new ModuleSpec(
NetworkingModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new NetworkingModule(context);
}
}),
new ModuleSpec(
NetInfoModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new NetInfoModule(context);
}
}),
new ModuleSpec(
PermissionsModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new PermissionsModule(context);
}
}),
new ModuleSpec(
ShareModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ShareModule(context);
}
}),
new ModuleSpec(
StatusBarModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new StatusBarModule(context);
}
}),
new ModuleSpec(
TimePickerDialogModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new TimePickerDialogModule(context);
}
}),
new ModuleSpec(
ToastModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new ToastModule(context);
}
}),
new ModuleSpec(
VibrationModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new VibrationModule(context);
}
}),
new ModuleSpec(
WebSocketModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new WebSocketModule(context);
}
}));
}
@Override