Skip to content

Latest commit

 

History

History
286 lines (223 loc) · 7.08 KB

readme.md

File metadata and controls

286 lines (223 loc) · 7.08 KB

react-native-mo-fs

Provide file system access and integration

Installation

Install just like your ordinary react-native module.

Usage

Please check the example/ code.

import { Fs } from 'react-native-mo-fs';

// for debugging:
Fs.setVerbose(true);

Basic ideas

  • Fs.readFile / Fs.writeFile all work with react-native's Blobs. These store the data on the native side and only pass a handle to javascript to avoid transferring lots of data using base64 encoding. As long as react-native does not support UInt8Array to NSData*/byte[] bridging this is the fastest solution.

  • You can read out or create blobs using Fs.readBlob and Fs.createBlob using encodings like base64, utf8 or arraybuffers. If using arraybuffers the buffer has to be converted to base64 internally first.

  • Most of the time you do not need the data on the javascript side. You can load a file to a blob and use that blob as payload data for fetch() or the other way around.

  • There is some assorted stuff in the library for hashing, image manipulation and similar stuff. The base idea is to support only operations that do not require additional native libraries to not bloat this library.

System Paths

console.log(Fs.paths);
// cache: Path; // a cache folder
// docs: Path; // the document folder
// bundle?: Path; // ios bundle folder
// document?: Path;
// caches?: Path;
// externalCache?: Path;
// files?: Path;
// packageResource?: Path;
// data?: Path;

Working with files

Reading files

const myBlob = await Fs.readFile(Fs.paths.docs + '/hello.jpg');
const myText = await Fs.readTextFile(Fs.paths.docs + '/hello.txt');

Writing files

await Fs.writeFile(Fs.paths.docs + '/hello.jpg', myBlob);
await Fs.writeTextFile(Fs.paths.docs + '/dump.json', JSON.stringify(data));
await Fs.appendFile(Fs.paths.docs + '/hello.jpg', myBlob);
await Fs.appendTextFile(Fs.paths.docs + '/log.txt', 'my log line\n');

File handling

await Fs.deleteFile(someFilePath);
await Fs.deleteFile(someFolderPath, true); // delete recursive
await Fs.renameFile(oldPath, newPath);
await Fs.listDir(folderPath); // returns array of file names
await Fs.createDir(Fs.paths.docs + '/logs'); // does not create parents
const stat = await Fs.stat(Fs.path.docs + '/somefile.txt');
if (!stat.exists) {
  console.log('not found');
} else if (stat.dir) {
  console.log('is a dir');
} else {
  console.log('is a file with size', stat.size, 'modified', new Date(stat.modified));
}

Working with Blobs

Get URL for Blob

const url = Fs.getBlobURL(myBlob);
return (<Image source={{ uri: url }} />);

Get contents of Blob

const base64_data = await Fs.readBlob(myBlob, 'base64');
const string = await Fs.readBlob(myBlob, 'utf8');
const arrayBuffer = await Fs.readBlob(myBlob, 'arraybuffer');

Create Blob from data

const myBlob = await Fs.createBlob('aGVsbG8gd29ybGQ=', 'base64');
const myBlob = await Fs.createBlob('hello world', 'utf8');
const myBlob = await Fs.createBlob(new Uint8Array([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]), 'arraybuffer');

Sharing / picking files

Share / View file

// Share a file (open-in in ios, share intent in android)
await Fs.shareFile(Fs.paths.docs + '/myimage.png');

// View a file (preview in ios, view intent in android)
await Fs.viewFile(Fs.paths.docs + '/myimage.png');

Pick file

// pick any single file
const urls = await Fs.pickFile({ multiple: false })

// pick one or more jpeg or png images
const urls = await Fs.pickFile({ multiple: true, types: ['image/jpeg', 'image/png'] })

// read the response
if (urls.length > 0) {
  const blob = await Fs.readURL(urls[0]);
  // do something
}

Pick/capture image/video

const res = await Fs.pickMedia({ });
const res = await Fs.captureMedia({ });
if (res) {
  const blob = await Fs.readURL(res.url);
  res.release();
}

iTunes file sharing

Add this to your Info.plist:

<key>UIFileSharingEnabled</key>
<true/>

And write stuff to Fs.paths.docs

Handling "Open In"

Add to android manifest:

<intent-filter>
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  UPDATE THIS
  <data android:scheme="file" />
  <data android:host="*" />
  <data android:pathPattern=".*\\.pdf" />
  <data android:mimeType="application/pdf" />
</intent-filter>

Add to ios Info.plist:

<key>CFBundleDocumentTypes</key>
<array>
  <dict>
    <key>CFBundleTypeName</key>
    <string>PDF</string>
    <key>LSHandlerRank</key>
    <string>Alternate</string>
    <key>LSItemContentTypes</key>
    <array>
        <string>com.adobe.pdf</string>
    </array>
  </dict>
</array>

Subscribe to the Event:

Fs.openFile.subscribe((event) => {
  console.log('requested to open url', event.url);
});

If the app was started initially due to an open event, the event handler is called as soon as the first subscriber is there.

Assorted stuff

Mime Types and EXIF

const mimeType = await Fs.getMimeType('png');
const mimeType = await Fs.getMimeType('blah.png');

// exif is not really done yet but might work for you ;)
const exif = await Fs.getExif(myBlob);

const extension = await Fs.getExtensionForMimeType('image/png'); // returns png

Blob hash

console.log('md5-hex', await Fs.getBlobHash(myBlob, 'md5'));
console.log('sha1-hex', await Fs.getBlobHash(myBlob, 'sha1'));
console.log('sha256-hex', await Fs.getBlobHash(myBlob, 'sha256'));

Images

const myImageBlob = await Fs.readFile(Fs.paths.docs + '/image.jpg');

const size = await Fs.getImageSize(myImageBlob);
console.log('the image size is', size.width, size.height);

const smallBlob = await Fs.resizeImage(myImageBlob, {
  maxWidth: 128,
  maxHeight: 128,
  fill: true, // make result 128x128 and keep aspect. otherwise result image is smaller
  encoding: 'jpeg', // or png, or webp
  quality: 0.3, // 0..1
});

const thumbnail = await Fs.createThumbnail(imageOrVideoBlob, {
  maxWidth: 128,
  maxHeight: 128,
  fill: true,
});

Backups

For android you need to specify the files to be backed up in your manifest:

<application
  android:fullBackupContent="@xml/backup_descriptor"
  android:restoreAnyVersion="true"
/>

Set restoreAnyVersion only if you are able to migrate from an older backup.

The contents of res/xml/backup_descriptor is something like:

<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
  <!-- for asyncstorage -->
  <include domain="database" path="RKStorage" />
  <!-- some path relative to data dir -->
  <include domain="root" path="some/path" />
</full-backup-content>

You can test backups using: adb shell bmgr backupnow your.package

Dev builds should be restored after uninstalling and redeploying from android studio.

For iOS pretty much everything is backed up unless excluded.

You can use (experimental):

Fs.ios.Module.setResourceValues('file:///path/to/file', {
  'NSURLIsExcludedFromBackupKey': true,
});

You can use app offloading to test backup / restore in dev.

Notes

  • iOS application:openURL: is swizzled on startup.