react-native-file-access
Version:
Filesystem access for React Native
288 lines (281 loc) • 8.13 kB
JavaScript
import { NativeModules, Platform } from 'react-native';
import { NativeEventEmitter } from 'react-native';
export { Util } from './util';
const LINKING_ERROR = `The package 'react-native-file-access' doesn't seem to be linked. Make sure: \n\n` + Platform.select({
ios: "- You have run 'pod install'\n",
default: ''
}) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go\n';
// @ts-expect-error
const isTurboModuleEnabled = global.__turboModuleProxy != null;
const FileAccessModule = isTurboModuleEnabled ? require('./NativeFileAccess').default : NativeModules.FileAccess;
const FileAccessNative = FileAccessModule ? FileAccessModule : new Proxy({}, {
get() {
throw new Error(LINKING_ERROR);
}
});
/**
* ID tracking next fetch request.
*/
let nextRequestId = 1;
/**
* Process fetch events for the request.
*/
function registerFetchListener(requestId, resolve, reject, onProgress) {
const eventEmitter = new NativeEventEmitter(FileAccessModule);
const listener = eventEmitter.addListener('FetchEvent', event => {
if (event.requestId !== requestId) {
return;
}
if (event.state === 'progress') {
onProgress === null || onProgress === void 0 ? void 0 : onProgress(event.bytesRead, event.contentLength, event.done);
} else if (event.state === 'error') {
listener.remove();
reject(new Error(event.message));
} else if (event.state === 'complete') {
listener.remove();
const headersLower = new Map();
for (const [key, value] of Object.entries(event.headers)) {
headersLower.set(key.toLowerCase(), value);
}
resolve({
getHeader: header => headersLower.get(header.toLowerCase()),
headers: event.headers,
ok: event.ok,
redirected: event.redirected,
status: event.status,
statusText: event.statusText,
url: event.url
});
}
});
}
/**
* Get a `Promise` that will resolve after the specified timespan.
*/
function sleep(milliseconds) {
return new Promise(resolve => setTimeout(resolve, milliseconds));
}
/**
* Periodically report file copy status to the progress listener.
*/
async function wrapCpListener(source, target, completion, onProgress) {
const sourceStat = await FileSystem.stat(source);
while (true) {
const targetStat = await Promise.race([completion.then(() => true), sleep(150).then(() => FileSystem.stat(target)).catch(() => false)]);
if (targetStat === true) {
onProgress(sourceStat.size, sourceStat.size, true);
break; // Process completed.
} else if (targetStat !== false) {
onProgress(targetStat.size, sourceStat.size, false);
}
}
}
export const FileSystem = {
/**
* Append content to a file.
*
* Default encoding of `data` is assumed utf8.
*/
appendFile(path, data) {
let encoding = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'utf8';
return FileAccessNative.appendFile(path, data, encoding);
},
/**
* Append a file to another file.
*
* Returns number of bytes written.
*/
concatFiles(source, target) {
return FileAccessNative.concatFiles(source, target);
},
/**
* Copy a file.
*/
cp(source, target, onProgress) {
const res = FileAccessNative.cp(source, target);
return onProgress == null ? res : wrapCpListener(source, target, res, onProgress);
},
/**
* Copy a bundled asset file.
*
* When using Android asset type 'resource', include the folder, but skip the
* file extension. For example use 'raw/foo', for the file 'res/raw/foo.txt'.
* When possible, prefer using the 'assets/' folder; files in 'res/' have
* naming restrictions imposed by Android.
* https://developer.android.com/guide/topics/resources/providing-resources.html#OriginalFiles
*/
cpAsset(asset, target) {
let type = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'asset';
return FileAccessNative.cpAsset(asset, target, type);
},
/**
* Copy a file to an externally controlled location.
*
* On Android API level < 29, may require permission WRITE_EXTERNAL_STORAGE.
*/
cpExternal(source, targetName, dir) {
return FileAccessNative.cpExternal(source, targetName, dir);
},
/**
* Check device available space.
*/
df() {
return FileAccessNative.df();
},
/**
* Check if a path exists.
*/
exists(path) {
return FileAccessNative.exists(path);
},
/**
* Save a network request to a file.
*/
async fetch(resource, init, onProgress) {
const requestId = nextRequestId++;
return new Promise((resolve, reject) => {
registerFetchListener(requestId, resolve, reject, onProgress);
FileAccessNative.fetch(requestId, resource, init);
});
},
/**
* Save a network request to a file.
*/
fetchManaged(resource, init, onProgress) {
const requestId = nextRequestId++;
return {
cancel: () => FileAccessNative.cancelFetch(requestId),
result: new Promise((resolve, reject) => {
registerFetchListener(requestId, resolve, reject, onProgress);
FileAccessNative.fetch(requestId, resource, init);
})
};
},
/**
* Return the local storage directory for app groups.
*
* This is an Apple only feature.
*/
getAppGroupDir(groupName) {
return FileAccessNative.getAppGroupDir(groupName);
},
/**
* Create a hard link.
*
* Creates a hard link at target pointing to source.
*/
hardlink(source, target) {
return FileAccessNative.hardlink(source, target);
},
/**
* Hash the file content.
*/
hash(path, algorithm) {
return FileAccessNative.hash(path, algorithm);
},
/**
* Check if a path is a directory.
*/
isDir(path) {
return FileAccessNative.isDir(path);
},
/**
* List files in a directory.
*/
ls(path) {
return FileAccessNative.ls(path);
},
/**
* Make a new directory.
*
* Returns path of the created directory.
*/
mkdir(path) {
return FileAccessNative.mkdir(path);
},
/**
* Move a file.
*/
mv(source, target) {
return FileAccessNative.mv(source, target);
},
/**
* Read the content of a file.
*/
readFile(path) {
let encoding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'utf8';
return FileAccessNative.readFile(path, encoding);
},
/**
* Read a chunk of the content of a file.
*/
readFileChunk(path, offset, length) {
let encoding = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'utf8';
return FileAccessNative.readFileChunk(path, offset, length, encoding);
},
/**
* Read file metadata.
*/
stat(path) {
return FileAccessNative.stat(path);
},
/**
* Read metadata of all files in a directory.
*/
statDir(path) {
return FileAccessNative.statDir(path);
},
/**
* Create a symbolic link.
*
* Creates a symbolic link at target pointing to source.
*/
symlink(source, target) {
return FileAccessNative.symlink(source, target);
},
/**
* Delete a file.
*/
unlink(path) {
return FileAccessNative.unlink(path);
},
/**
* Extract a zip archive.
*/
unzip(source, target) {
return FileAccessNative.unzip(source, target);
},
/**
* Write content to a file.
*
* Default encoding of `data` is assumed utf8.
*/
writeFile(path, data) {
let encoding = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'utf8';
return FileAccessNative.writeFile(path, data, encoding);
}
};
/**
* Directory constants.
*/
export const Dirs = FileAccessModule ? FileAccessNative.getConstants() : new Proxy({
CacheDir: '',
DocumentDir: '',
MainBundleDir: ''
}, {
get() {
throw new Error(LINKING_ERROR);
}
});
/**
* Utility functions for working with Android scoped storage.
*/
export const AndroidScoped = {
/**
* Append a path segment to an Android scoped storage content uri.
*/
appendPath(basePath, segment) {
return basePath + encodeURIComponent('/' + segment);
}
};
//# sourceMappingURL=index.js.map