react-native-fs/FS.common.js

443 lines
16 KiB
JavaScript

/**
* React Native FS
* @flow
*/
'use strict';
// This file supports both iOS and Android
var RNFSManager = require('react-native').NativeModules.RNFSManager;
var NativeAppEventEmitter = require('react-native').NativeAppEventEmitter; // iOS
var DeviceEventEmitter = require('react-native').DeviceEventEmitter; // Android
var base64 = require('base-64');
var utf8 = require('utf8');
var RNFSFileTypeRegular = RNFSManager.RNFSFileTypeRegular;
var RNFSFileTypeDirectory = RNFSManager.RNFSFileTypeDirectory;
var jobId = 0;
var getJobId = () => {
jobId += 1;
return jobId;
};
var normalizeFilePath = (path: string) => (path.startsWith('file://') ? path.slice(7) : path);
type MkdirOptions = {
NSURLIsExcludedFromBackupKey?: boolean; // iOS only
};
type ReadDirItem = {
created: date; // The creation date of the file (iOS only)
modified: date; // The last modified date of the file
name: string; // The name of the item
path: string; // The absolute path to the item
size: string; // Size in bytes
isFile: () => boolean; // Is the file just a file?
isDirectory: () => boolean; // Is the file a directory?
};
type StatResult = {
name: string; // The name of the item
path: string; // The absolute path to the item
size: string; // Size in bytes
mode: number; // UNIX file mode
ctime: number; // Created date
utime: number; // Updated date
isFile: () => boolean; // Is the file just a file?
isDirectory: () => boolean; // Is the file a directory?
};
type Headers = { [name: string]: string };
type Fields = { [name: string]: string };
type DownloadFileOptions = {
fromUrl: string; // URL to download file from
toFile: string; // Local filesystem path to save the file to
headers?: Headers; // An object of headers to be passed to the server
background?: boolean; // iOS only
progressDivider?: number;
begin?: (res: DownloadBeginCallbackResult) => void;
progress?: (res: DownloadProgressCallbackResult) => void;
};
type DownloadBeginCallbackResult = {
jobId: number; // The download job ID, required if one wishes to cancel the download. See `stopDownload`.
statusCode: number; // The HTTP status code
contentLength: number; // The total size in bytes of the download resource
headers: Headers; // The HTTP response headers from the server
};
type DownloadProgressCallbackResult = {
jobId: number; // The download job ID, required if one wishes to cancel the download. See `stopDownload`.
contentLength: number; // The total size in bytes of the download resource
bytesWritten: number; // The number of bytes written to the file so far
};
type DownloadResult = {
jobId: number; // The download job ID, required if one wishes to cancel the download. See `stopDownload`.
statusCode: number; // The HTTP status code
bytesWritten: number; // The number of bytes written to the file
};
type UploadFileOptions = {
toUrl: string; // URL to upload file to
files: UploadFileItem[]; // An array of objects with the file information to be uploaded.
headers?: Headers; // An object of headers to be passed to the server
fields?: Fields; // An object of fields to be passed to the server
method?: string; // Default is 'POST', supports 'POST' and 'PUT'
begin?: (res: UploadBeginCallbackResult) => void;
progress?: (res: UploadProgressCallbackResult) => void;
};
type UploadFileItem = {
name: string; // Name of the file, if not defined then filename is used
filename: string; // Name of file
filepath: string; // Path to file
filetype: string; // The mimetype of the file to be uploaded, if not defined it will get mimetype from `filepath` extension
};
type UploadBeginCallbackResult = {
jobId: number; // The upload job ID, required if one wishes to cancel the upload. See `stopUpload`.
};
type UploadProgressCallbackResult = {
jobId: number; // The upload job ID, required if one wishes to cancel the upload. See `stopUpload`.
totalBytesExpectedToSend: number; // The total number of bytes that will be sent to the server
totalBytesSent: number; // The number of bytes sent to the server
};
type UploadResult = {
jobId: number; // The upload job ID, required if one wishes to cancel the upload. See `stopUpload`.
statusCode: number; // The HTTP status code
headers: Headers; // The HTTP response headers from the server
body: string; // The HTTP response body
};
type FSInfoResult = {
totalSpace: number; // The total amount of storage space on the device (in bytes).
freeSpace: number; // The amount of available storage space on the device (in bytes).
};
/**
* Generic function used by readFile and readFileAssets
*/
function readFileGeneric(filepath: string, encodingOrOptions:?string, command: Function) {
var options = {
encoding: 'utf8'
};
if (encodingOrOptions) {
if (typeof encodingOrOptions === 'string') {
options.encoding = encodingOrOptions;
} else if (typeof encodingOrOptions === 'object') {
options = encodingOrOptions;
}
}
return command(normalizeFilePath(filepath)).then((b64) => {
var contents;
if (options.encoding === 'utf8') {
contents = utf8.decode(base64.decode(b64));
} else if (options.encoding === 'ascii') {
contents = base64.decode(b64);
} else if (options.encoding === 'base64') {
contents = b64;
} else {
throw new Error('Invalid encoding type "' + String(options.encoding) + '"');
}
return contents;
});
}
/**
* Generic function used by readDir and readDirAssets
*/
function readDirGeneric(dirpath: string, command: Function) {
return command(normalizeFilePath(dirpath)).then(files => {
return files.map(file => ({
ctime: file.ctime && new Date(file.ctime * 1000) || null,
mtime: new Date(file.mtime * 1000),
name: file.name,
path: file.path,
size: file.size,
isFile: () => file.type === RNFSFileTypeRegular,
isDirectory: () => file.type === RNFSFileTypeDirectory,
}));
});
}
var RNFS = {
mkdir(filepath: string, options: MkdirOptions = {}): Promise<void> {
return RNFSManager.mkdir(normalizeFilePath(filepath), options).then(() => void 0);
},
moveFile(filepath: string, destPath: string): Promise<void> {
return RNFSManager.moveFile(normalizeFilePath(filepath), normalizeFilePath(destPath)).then(() => void 0);
},
copyFile(filepath: string, destPath: string): Promise<void> {
return RNFSManager.copyFile(normalizeFilePath(filepath), normalizeFilePath(destPath)).then(() => void 0);
},
pathForBundle(bundleNamed: string): Promise<string> {
return RNFSManager.pathForBundle(bundleNamed);
},
getFSInfo(): Promise<FSInfoResult> {
return RNFSManager.getFSInfo();
},
unlink(filepath: string): Promise<void> {
return RNFSManager.unlink(normalizeFilePath(filepath)).then(() => void 0);
},
exists(filepath: string): Promise<boolean> {
return RNFSManager.exists(normalizeFilePath(filepath));
},
stopDownload(jobId: number): void {
RNFSManager.stopDownload(jobId);
},
stopUpload(jobId: number): void {
RNFSManager.stopUpload(jobId);
},
readDir(dirpath: string): Promise<ReadDirItem[]> {
return readDirGeneric(dirpath, RNFSManager.readDir);
},
// Android-only
readDirAssets(dirpath: string): Promise<ReadDirItem[]> {
if (!RNFSManager.readDirAssets) {
throw new Error('readDirAssets is not available on this platform');
}
return readDirGeneric(dirpath, RNFSManager.readDirAssets);
},
// Android-only
existsAssets(filepath: string) {
if (!RNFSManager.existsAssets) {
throw new Error('existsAssets is not available on this platform');
}
return RNFSManager.existsAssets(filepath);
},
// Node style version (lowercase d). Returns just the names
readdir(dirpath: string): Promise<string[]> {
return RNFS.readDir(normalizeFilePath(dirpath)).then(files => {
return files.map(file => file.name);
});
},
stat(filepath: string): Promise<StatResult> {
return RNFSManager.stat(normalizeFilePath(filepath)).then((result) => {
return {
'ctime': new Date(result.ctime * 1000),
'mtime': new Date(result.mtime * 1000),
'size': result.size,
'mode': result.mode,
isFile: () => result.type === RNFSFileTypeRegular,
isDirectory: () => result.type === RNFSFileTypeDirectory,
};
});
},
readFile(filepath: string, encodingOrOptions?: any): Promise<string> {
return readFileGeneric(filepath, encodingOrOptions, RNFSManager.readFile);
},
// Android only
readFileAssets(filepath: string, encodingOrOptions?: any): Promise<string> {
if (!RNFSManager.readFileAssets) {
throw new Error('readFileAssets is not available on this platform');
}
return readFileGeneric(filepath, encodingOrOptions, RNFSManager.readFileAssets);
},
hash(filepath: string, algorithm: string): Promise<string> {
return RNFSManager.hash(filepath, algorithm);
},
// Android only
copyFileAssets(filepath: string, destPath:string) {
if (!RNFSManager.copyFileAssets) {
throw new Error('copyFileAssets is not available on this platform');
}
return RNFSManager.copyFileAssets(normalizeFilePath(filepath), normalizeFilePath(destPath)).then(() => void 0);
},
// iOS only
// Copies fotos from asset-library (camera-roll) to a specific location
// with a given width or height
// @see: https://developer.apple.com/reference/photos/phimagemanager/1616964-requestimageforasset
copyAssetsFileIOS(imageUri: string, destPath: string, width: number, height: number,
scale : number = 1.0, compression : number = 1.0, resizeMode : string = 'contain' ): Promise<string> {
return RNFSManager.copyAssetsFileIOS(imageUri, destPath, width, height, scale, compression, resizeMode );
},
writeFile(filepath: string, contents: string, encodingOrOptions?: any): Promise<void> {
var b64;
var options = {
encoding: 'utf8'
};
if (encodingOrOptions) {
if (typeof encodingOrOptions === 'string') {
options.encoding = encodingOrOptions;
} else if (typeof encodingOrOptions === 'object') {
options = encodingOrOptions;
}
}
if (options.encoding === 'utf8') {
b64 = base64.encode(utf8.encode(contents));
} else if (options.encoding === 'ascii') {
b64 = base64.encode(contents);
} else if (options.encoding === 'base64') {
b64 = contents;
} else {
throw new Error('Invalid encoding type "' + options.encoding + '"');
}
return RNFSManager.writeFile(normalizeFilePath(filepath), b64).then(() => void 0);
},
appendFile(filepath: string, contents: string, encodingOrOptions?: any): Promise<void> {
var b64;
var options = {
encoding: 'utf8'
};
if (encodingOrOptions) {
if (typeof encodingOrOptions === 'string') {
options.encoding = encodingOrOptions;
} else if (typeof encodingOrOptions === 'object') {
options = encodingOrOptions;
}
}
if (options.encoding === 'utf8') {
b64 = base64.encode(utf8.encode(contents));
} else if (options.encoding === 'ascii') {
b64 = base64.encode(contents);
} else if (options.encoding === 'base64') {
b64 = contents;
} else {
throw new Error('Invalid encoding type "' + options.encoding + '"');
}
return RNFSManager.appendFile(normalizeFilePath(filepath), b64);
},
downloadFile(options: DownloadFileOptions): { jobId: number, promise: Promise<DownloadResult> } {
if (typeof options !== 'object') throw new Error('downloadFile: Invalid value for argument `options`');
if (typeof options.fromUrl !== 'string') throw new Error('downloadFile: Invalid value for property `fromUrl`');
if (typeof options.toFile !== 'string') throw new Error('downloadFile: Invalid value for property `toFile`');
if (options.headers && typeof options.headers !== 'object') throw new Error('downloadFile: Invalid value for property `headers`');
if (options.background && typeof options.background !== 'boolean') throw new Error('downloadFile: Invalid value for property `background`');
if (options.progressDivider && typeof options.progressDivider !== 'number') throw new Error('downloadFile: Invalid value for property `progressDivider`');
var jobId = getJobId();
var subscriptions = [];
if (options.begin) {
subscriptions.push(NativeAppEventEmitter.addListener('DownloadBegin-' + jobId, options.begin));
}
if (options.progress) {
subscriptions.push(NativeAppEventEmitter.addListener('DownloadProgress-' + jobId, options.progress));
}
var bridgeOptions = {
jobId: jobId,
fromUrl: options.fromUrl,
toFile: normalizeFilePath(options.toFile),
headers: options.headers || {},
background: !!options.background,
progressDivider: options.progressDivider || 0
};
return {
jobId,
promise: RNFSManager.downloadFile(bridgeOptions).then(res => {
subscriptions.forEach(sub => sub.remove());
return res;
})
};
},
uploadFiles(options: UploadFileOptions): { jobId: number, promise: Promise<UploadResult> } {
if (!RNFSManager.uploadFiles) {
return {
jobId: -1,
promise: Promise.reject(new Error('`uploadFiles` is unsupported on this platform'))
};
}
var jobId = getJobId();
var subscriptions = [];
if (typeof options !== 'object') throw new Error('uploadFiles: Invalid value for argument `options`');
if (typeof options.toUrl !== 'string') throw new Error('uploadFiles: Invalid value for property `toUrl`');
if (!Array.isArray(options.files)) throw new Error('uploadFiles: Invalid value for property `files`');
if (options.headers && typeof options.headers !== 'object') throw new Error('uploadFiles: Invalid value for property `headers`');
if (options.fields && typeof options.fields !== 'object') throw new Error('uploadFiles: Invalid value for property `fields`');
if (options.method && typeof options.method !== 'string') throw new Error('uploadFiles: Invalid value for property `method`');
if (options.begin) {
subscriptions.push(NativeAppEventEmitter.addListener('UploadBegin-' + jobId, options.begin));
}
if (options.beginCallback && options.beginCallback instanceof Function) {
// Deprecated
subscriptions.push(NativeAppEventEmitter.addListener('UploadBegin-' + jobId, options.beginCallback));
}
if (options.progress) {
subscriptions.push(NativeAppEventEmitter.addListener('UploadProgress-' + jobId, options.progress));
}
if (options.progressCallback && options.progressCallback instanceof Function) {
// Deprecated
subscriptions.push(NativeAppEventEmitter.addListener('UploadProgress-' + jobId, options.progressCallback));
}
var bridgeOptions = {
jobId: jobId,
toUrl: options.toUrl,
files: options.files,
headers: options.headers || {},
fields: options.fields || {},
method: options.method || 'POST'
};
return {
jobId,
promise: RNFSManager.uploadFiles(bridgeOptions).then(res => {
subscriptions.forEach(sub => sub.remove());
return res;
})
};
},
MainBundlePath: RNFSManager.RNFSMainBundlePath,
CachesDirectoryPath: RNFSManager.RNFSCachesDirectoryPath,
DocumentDirectoryPath: RNFSManager.RNFSDocumentDirectoryPath,
ExternalDirectoryPath: RNFSManager.RNFSExternalDirectoryPath,
ExternalStorageDirectoryPath: RNFSManager.RNFSExternalStorageDirectoryPath,
TemporaryDirectoryPath: RNFSManager.RNFSTemporaryDirectoryPath,
LibraryDirectoryPath: RNFSManager.RNFSLibraryDirectoryPath,
PicturesDirectoryPath: RNFSManager.RNFSPicturesDirectoryPath
};
module.exports = RNFS;