From 826dda5a754e5981d0045dbb8389ef23d59a88c3 Mon Sep 17 00:00:00 2001 From: Jan Blunck <238088+jblunck@users.noreply.github.com> Date: Thu, 7 Jan 2021 17:36:07 +0100 Subject: [PATCH] Mobile: Fixes #4268: Fix "Not implemented" error when downloading resources with S3 sync target (#4279) Since the RNFetchBlob API doesn't support writing binary data directly it creates a custom Writable which is doing the base64 encoding per chunk. This also fixes a problem with the S3 synchronization code using the shim.fsDriver().writeBinaryFile(). Tested with AVD Android 10.0 target. Signed-off-by: Jan Blunck --- packages/app-mobile/index.js | 3 +++ packages/app-mobile/package-lock.json | 21 +++++++++++++++++++++ packages/app-mobile/package.json | 3 ++- packages/app-mobile/utils/fs-driver-rn.js | 23 +++++++++++++++++++++-- 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/packages/app-mobile/index.js b/packages/app-mobile/index.js index d68426687..3139e7088 100644 --- a/packages/app-mobile/index.js +++ b/packages/app-mobile/index.js @@ -44,3 +44,6 @@ LogBox.ignoreLogs([ ]); AppRegistry.registerComponent('Joplin', () => Root); + +// Using streams on react-native requires to polyfill process.nextTick() +global.process.nextTick = setImmediate; diff --git a/packages/app-mobile/package-lock.json b/packages/app-mobile/package-lock.json index ba8e27733..b5e553ca7 100644 --- a/packages/app-mobile/package-lock.json +++ b/packages/app-mobile/package-lock.json @@ -8379,6 +8379,27 @@ "emitter-component": "^1.1.1" } }, + "stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "requires": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "stream-buffers": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", diff --git a/packages/app-mobile/package.json b/packages/app-mobile/package.json index ac677226c..ade1c21a2 100644 --- a/packages/app-mobile/package.json +++ b/packages/app-mobile/package.json @@ -53,14 +53,15 @@ "redux": "4.0.0", "rn-fetch-blob": "^0.12.0", "stream": "0.0.2", + "stream-browserify": "^3.0.0", "string-natural-compare": "^2.0.2", "timers": "^0.1.1", "valid-url": "^1.0.9" }, "devDependencies": { - "@joplin/tools": "^1.0.9", "@babel/core": "^7.11.6", "@babel/runtime": "^7.11.2", + "@joplin/tools": "^1.0.9", "@types/node": "^14.14.6", "@types/react": "^16.9.55", "execa": "^4.0.0", diff --git a/packages/app-mobile/utils/fs-driver-rn.js b/packages/app-mobile/utils/fs-driver-rn.js index 96b188477..89c9fcf5b 100644 --- a/packages/app-mobile/utils/fs-driver-rn.js +++ b/packages/app-mobile/utils/fs-driver-rn.js @@ -1,6 +1,8 @@ const RNFS = require('react-native-fs'); const FsDriverBase = require('@joplin/lib/fs-driver-base').default; const RNFetchBlob = require('rn-fetch-blob').default; +const { Writable } = require('stream-browserify'); +const { Buffer } = require('buffer'); class FsDriverRN extends FsDriverBase { appendFileSync() { @@ -24,8 +26,25 @@ class FsDriverRN extends FsDriverBase { return await this.unlink(path); } - writeBinaryFile() { - throw new Error('Not implemented'); + writeBinaryFile(path, content) { + const buffer = Buffer.from(content); + return RNFetchBlob.fs.writeStream(path, 'base64').then(stream => { + const fileStream = new Writable({ + write(chunk, encoding, callback) { + this.stream.write(chunk.toString('base64')); + callback(); + }, + final(callback) { + this.stream.close(); + callback(); + }, + }); + // using options.construct is not implemented in readable-stream so lets + // pass the stream from RNFetchBlob to the Writable instance here + fileStream.stream = stream; + fileStream.write(buffer); + fileStream.end(); + }); } // Returns a format compatible with Node.js format