目录
在移动端实际开发中难免会涉及涉及缓存:
a.AsyncStorage:一些简单文本、用户身份Token或者JSON文件等可以用AsyncStorage,类似h5的数据存储LocalStorage;在 iOS 上,AsyncStorage在原生端的实现是把较小值存放在序列化的字典中,而把较大值写入单独的文件。在 Android 上,AsyncStorage会尝试使用RocksDB,或退而选择 SQLite。
可参考:
https://blog.csdn.net/ahou2468/article/details/88954215
b.Realm:Realm不是ORM(对象关系映射数据库),也不是建立在sqlite之上。相反,我们为移动应用程序开发人员构建了一个完整的数据库,它使用动态映射到完整的自定义数据库引擎(而不仅仅是一个键值存储)的本机JavaScript对象。这允许我们在保持性能的同时提供一个简单的API。使用Realm,您可以建模复杂的数据,链接图形中的对象,以及组成高级查询。
可参考:
https://blog.csdn.net/ahou2468/article/details/94384048
c.文件存储(react-native-fs):
实现了react native的访问本地文件系统
主要介绍一下本地文件存储(react-native-fs);
注意事项:
对于rn<0.57和/或gradle<3,您必须安装react native fs的版本2.11.17!
对于rn>=0.57和/或gradle>=3,您必须时安装react native fs的版本2.13.2!
实现了react native的访问本地文件系统;
支持文件的创建、删除、查看、上传、下载;
a.安装(install)
npm install react-native-fs --save
b.链接(link)
react-native link react-native-fs
执行结果:
rnpm-install info Linking react-native-fs ios dependency
rnpm-install info Platform 'ios' module react-native-fs has been successfully linked
rnpm-install info Linking react-native-fs android dependency
rnpm-install info Platform 'android' module react-native-fs has been successfully linked
如果link失败,或者link之后仍旧不能import,则考虑手动link,详情查看官网教程,传送门。
文件创建,修改,读取,删除,上传,下载 ;
参数说明:
import RNFS from 'react-native-fs'
MainBundlePath (String) 主bundle目录的绝对路径; (在Android上不适合)
CachesDirectoryPath (String) 缓存目录的绝对路径;
ExternalCachesDirectoryPath (String) 外部缓存目录的绝对路径 (仅在Android上支持)
DocumentDirectoryPath (String) 文档目录的绝对路径
TemporaryDirectoryPath (String) 临时目录的绝对路径(返回到Android上的缓存目录)
LibraryDirectoryPath (String) NSLibraryDirectory绝对路径; (在iOS上支持)
ExternalDirectoryPath (String) 外部文件的绝对路径,共享目录(在android上支持)
ExternalStorageDirectoryPath (String) 外部存储的绝对路径,共享目录 (在android上支持)
Android打印日志:
MainBundlePath: undefined
CachesDirectoryPath: /data/user/0/com.helloworld/cache
ExternalCachesDirectoryPath: /storage/emulated/0/Android/data/com.helloworld/cache
DocumentDirectoryPath: /data/user/0/com.helloworld/files
ExternalDirectoryPath: /storage/emulated/0/Android/data/com.helloworld/files
ExternalStorageDirectoryPath: /storage/emulated/0
TemporaryDirectoryPath: /data/user/0/com.helloworld/cache
LibraryDirectoryPath: undefined
PicturesDirectoryPath: /storage/emulated/0/Pictures
FileProtectionKeys: undefined
android使用注意事项:
使用ExternalStorageDirectoryPath时,需要请求权限(在Android上)才能读取和写入外部存储;在Android23版本以后需要申请权限;申请权限示例:https://facebook.github.io/react-native/docs/permissionsandroid
IOS的打印日志
MainBundlePath: /Users/fandong/Library/Developer/CoreSimulator/Devices/2186A64F-
70D4-4BF2-A2F8-AE357F828D33/data/Containers/Bundle/Application/88D43269-12E3-4720-80D1-5618EBEF75DE/HelloWorld.app
CachesDirectoryPath: /Users/fandong/Library/Developer/CoreSimulator/Devices/2186A64F-70D4-4BF2-A2F8-
AE357F828D33/data/Containers/Data/Application/DA44823E-A4B6-4C4A-B020-294C75AF374D/Library/Caches
ExternalCachesDirectoryPath: undefined
DocumentDirectoryPath: /Users/fandong/Library/Developer/CoreSimulator/Devices/2186A64F-70D4-4BF2-A2F8-
AE357F828D33/data/Containers/Data/Application/DA44823E-A4B6-4C4A-B020-294C75AF374D/Documents
ExternalDirectoryPath: null
ExternalStorageDirectoryPath: null
TemporaryDirectoryPath: /Users/fandong/Library/Developer/CoreSimulator/Devices/2186A64F-70D4-4BF2-A2F8-
AE357F828D33/data/Containers/Data/Application/DA44823E-A4B6-4C4A-B020-294C75AF374D/tmp/
LibraryDirectoryPath: /Users/fandong/Library/Developer/CoreSimulator/Devices/2186A64F-70D4-4BF2-A2F8-
AE357F828D33/data/Containers/Data/Application/DA44823E-A4B6-4C4A-B020-
294C75AF374D/Library
PicturesDirectoryPath: undefined
FileProtectionKeys: undefined
这里可以简单了解一下ios的沙盒模型。
1、Documents 目录:您应该将所有的应用程序数据文件写入到这个目录下。这个目录用于存储用户数据。该路径可通过配置实现iTunes共享文件。可被iTunes备份。
2、AppName.app 目录:这是应用程序的程序包目录,包含应用程序的本身。由于应用程序必须经过签名,所以您在运行时不能对这个目录中的内容进行修改,否则可能会使应用程序无法启动。
3、Library 目录:这个目录下有两个子目录:
3.1 Preferences 目录:包含应用程序的偏好设置文件。您不应该直接创建偏好设置文件,而是应该使用NSUserDefaults类来取得和设置应用程序的偏好.
3.2 Caches 目录:用于存放应用程序专用的支持文件,保存应用程序再次启动过程中需要的信息。
可创建子文件夹。可以用来放置您希望被备份但不希望被用户看到的数据。该路径下的文件夹,除Caches以外,都会被iTunes备份。
4、tmp 目录:这个目录用于存放临时文件,保存应用程序再次启动过程中不需要的信息。该路径下的文件不会被iTunes备份。
let path = RNFS.DocumentDirectoryPath + '/test.txt';
RNFS.writeFile(path, 'Lorem ipsum dolor sit amet', 'utf8')
.then((success)=>{
console.log('FILE WRITTEN'+' '+path);
})
.catch((err)=>{
console.log(err.message);
});
//创建删除路径
let delpath = RNFS.DocumentDirectoryPath + '/test.txt';
//执行删除
RNFS.unlink(delpath)
.then(()=>{
console.log('FILE DELETED');
//如果文件不存在,会抛出异常
}).catch((err)=>{
console.log(err.message);
});
File does not exist
//获取文件列表和目录
RNFS.readDir(RNFS.DocumentDirectoryPath)
.then((result)=>{
console.log('GOT RESULT', result);
// stat the second file,找到第二个 文件
return Promise.all([RNFS.stat(result[1].path), result[1].path]);
}).then((statResult)=>{
/**
* stat
*return {
'path': filepath,
'ctime': new Date(result.ctime * 1000),
'mtime': new Date(result.mtime * 1000),
'size': result.size,
'mode': result.mode,
'originalFilepath': result.originalFilepath,
isFile: () => result.type === RNFSFileTypeRegular,
isDirectory: () => result.type === RNFSFileTypeDirectory,
};
*/
// if we have a file, read it
if(statResult[0].isFile()){ //返回的是数组,第一个是对象,第二个是文件
return RNFS.readFile(statResult[1], 'utf8');
}
// console.log(statResult[0].path);
return 'no file';
}).then((contents)=>{
// log the file contents,输出文件内容
console.log(contents);
}).catch((err) => {
console.log(err.message, err.code);
});
文件读取方法:
读取文件ReadDirItem定义:
type ReadDirItem = {
ctime: date; // The creation date of the file (iOS only)
mtime: 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?
};
a.readDir(dirpath: string): Promise<ReadDirItem[]>
读取路径下的内容。这必须是绝对路径。使用下面路径常量形成可用的文件路径。
export const MainBundlePath: string
export const CachesDirectoryPath: string
export const ExternalCachesDirectoryPath: string
export const DocumentDirectoryPath: string
export const ExternalDirectoryPath: string
export const ExternalStorageDirectoryPath: string
export const TemporaryDirectoryPath: string
export const LibraryDirectoryPath: string
export const PicturesDirectoryPath: string
export const FileProtectionKeys: string
返回的Promise使用具有以下属性的对象数组解析:
type ReadDirItem = {
ctime: date; // The creation date of the file (iOS only)
mtime: 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?
};
b.readDirAssets(dirpath: string): Promise<ReadDirItem[]>(Android)
读取android应用程序的assets文件夹中dirpath的内容。dirpath是从assets文件夹根目录到文件的相对路径。
c.readdir(dirpath: string): Promise<string[]>
返回目录下的文件名数组:["movies.realm.management", "test.txt", "ReactNativeDevBundle.js", "movies.realm.lock", "movies.realm", "movies.realm.note"]
d.stat(filepath: string): Promise<StatResult>
在filepath查找文件信息。如果文件路径链接到虚拟文件(例如android content uri),则可以使用原始路径查找指向的文件路径。Promise使用具有以下属性的对象解析:
type StatResult = {
path: // The same as filepath argument
ctime: date; // The creation date of the file
mtime: date; // The last modified date of the file
size: string; // Size in bytes
mode: number; // UNIX file mode
originalFilepath: string; // ANDROID: In case of content uri this is the pointed file path, otherwise is the same as path
isFile: () => boolean; // Is the file just a file?
isDirectory: () => boolean; // Is the file a directory?
};
e.readFile(filepath: string, encoding?: string): Promise<string>
读取路径处的文件并返回内容。编码可以是utf8(默认)、ascii、base64之一。使用base64读取二进制文件。
注意如果读取大文件,会等待比较久;
f.read(filepath: string, length = 0, position = 0, encodingOrOptions?: any): Promise<string>
从路径处文件的给定位置读取长度字节并返回内容。编码可以是utf8(默认)、ascii、base64之一。使用base64读取二进制文件。
注意:分段读取大文件可以改善性能。
g.readFileAssets(filepath:string, encoding?: string): Promise<string>
读取android应用程序的assets文件夹中路径处的文件并返回内容。编码可以是utf8(默认)、ascii、base64之一。使用base64读取二进制文件。
file path是从assets文件夹根目录到文件的相对路径。
注:仅限Android。
h.readFileRes(filename:string, encoding?: string): Promise<string>
读取android应用的res文件夹中名为filename的文件并返回内容。res/drawable用作图像文件的父文件夹,res/raw用于其他所有文件。编码可以是utf8(默认)、ascii、base64之一。使用base64读取二进制文件。
注:仅限Android。
// require the module,导入react-native-fs库
var RNFS = require('react-native-fs');
var uploadUrl = 'http://requestb.in/XXXXXXX'; // For testing purposes, go to http://requestb.in/ and create your own link,测试上传路径
// create an array of objects of the files you want to upload
// 创建一个想要上传文件的数组
var files = [
{
name: 'test1',
filename: 'test1.w4a',
filepath: RNFS.DocumentDirectoryPath + '/test1.w4a',
filetype: 'audio/x-m4a'
}, {
name: 'test2',
filename: 'test2.w4a',
filepath: RNFS.DocumentDirectoryPath + '/test2.w4a',
filetype: 'audio/x-m4a'
}
];
//上传开始回调
var uploadBegin = (response) => {
var jobId = response.jobId;
console.log('UPLOAD HAS BEGUN! JobId: ' + jobId);
};
//上传进度回调
var uploadProgress = (response) => {
var percentage = Math.floor((response.totalBytesSent/response.totalBytesExpectedToSend) * 100);
console.log('UPLOAD IS ' + percentage + '% DONE!');
};
// upload files
//执行文件上传
RNFS.uploadFiles({
toUrl: uploadUrl,//文件上传路径
files: files, //上传的文件数组
method: 'POST', //HTTP请求方法
headers: {
'Accept': 'application/json', //请求header
},
fields: {
'hello': 'world',
},
begin: uploadBegin, //上传开始回调
progress: uploadProgress //上传进度回调
}).promise.then((response) => {//HTTP response响应
if (response.statusCode == 200) {
console.log('FILES UPLOADED!'); // response.statusCode状态码, response.headers响应header, response.body 响应body
} else {
console.log('SERVER ERROR');
}
})
.catch((err) => { //HTTP请求异常
if(err.description === "cancelled") {
// cancelled by user
}
console.log(err);
});
a.writeFile(filepath: string, contents: string, encoding?: string): Promise<void>
将内容写入到指定filepath下文件。编码可以是utf8(默认)、ascii、base64之一。选项可以接受指定文件属性的对象,如模式等。
b.appendFile(filepath: string, contents: string, encoding?: string): Promise<void>
将内容追加到filepath。编码可以是utf8(默认)、ascii、base64之一。
c.write(filepath: string, contents: string, position?: number, encoding?: string): Promise<void>
在给定的随机访问位置将内容写入filepath。当位置未定义或-1时,内容将附加到文件末尾。编码可以是utf8(默认)、ascii、base64之一。
a.moveFile(filepath: string, destPath: string): Promise<void>
移动文件到指定目录;
b.copyFile(filepath: string, destPath: string): Promise<void>
复制文件到指定路径 ;
更多复制文件操作方法参考:https://github.com/itinance/react-native-fs
a.exists(filepath: string): Promise<boolean>
检测指定路径下文件是否存在;
更多检测文件是否存在操作方法参考:https://github.com/itinance/react-native-fs
a.mkdir(filepath: string, options?: MkdirOptions): Promise<void>
type MkdirOptions = {
NSURLIsExcludedFromBackupKey?: boolean; // iOS only
};
在filepath创建一个目录。自动创建父级,如果已经存在则不抛出(类似于linux mkdir-p)。
(仅限iOS):可以提供nsurlisexcludedfrombackupkey属性以在iOS平台上设置此属性。苹果将拒绝存储没有此属性的离线缓存数据的应用程序。
a.文件下载
downloadFile(options: DownloadFileOptions): { jobId: number, promise: Promise<DownloadResult> }
DownloadFileOptions
type DownloadFileOptions = {
fromUrl: string; // 下载文件的url
toFile: string; // 本地保存文件的路径URL
headers?: Headers; // 传递给服务器的请求header
background?: boolean; // 进入后台后继续下载(在iOS上支持)
discretionary?: boolean; // 允许操作系统控制下载的时间和速度,以提高感知性能 (在iOS上支持)
cacheable?: boolean; // 下载是否可以存储在共享的nsurlcache中(在iOS上支持,默认为true)
progressDivider?: number;
begin?: (res: DownloadBeginCallbackResult) => void;
progress?: (res: DownloadProgressCallbackResult) => void;
resumable?: () => void; // 仅仅在IOS上支持
connectionTimeout?: number // 仅仅在Android上支持
readTimeout?: number // 在IOS和Android上都支持
};
DownloadResult
type DownloadResult = {
jobId: number; // The download job ID, required if one wishes to cancel
the download. See `stopDownload`.(生成的文件标识ID)
statusCode: number; // HTTP状态code
bytesWritten: number; // 被写入文件字节数量
};
从options.fromUrl下载文件到options.toFile。将覆盖任何以前存在的文件。
如果提供了options.begin,则当接收到头并传递具有以下属性的单个参数时,将在下载开始时调用它一次:
type DownloadBeginCallbackResult = {
jobId: number; // 下载文件ID, 当取消下载时需要用到文件ID. 见 `stopDownload`.
statusCode: number; // HTTP状态码
contentLength: number; // 实际下载文件大小
headers: Headers; // 来自于服务器HTTP的headers
};
如果提供了options.progress,则将连续调用它,并传递具有以下属性的单个参数:
type DownloadProgressCallbackResult = {
jobId: number; // 下载文件ID, 当取消下载时需要用到文件ID. 见 `stopDownload`.
contentLength: number; //下载资源的文件大小
bytesWritten: number; // 到目前为止被写入文件字节大小
};
如果提供了options.progressdivider,它将返回被progressdivider划分的进度事件。
例如,如果progressdivider=10,您将只收到10个进度值回调:0、10、20、30、40、50、60、70、80、90、100使用它解决性能问题。如果progressdivider=0,则将接收所有progresscallback调用,默认值为0。
(仅限iOS):options.background(boolean)-当应用程序未聚焦时是否继续下载(默认值:false)。此选项当前仅适用于iOS,请参阅后台下载教程(iOS)(https://github.com/itinance/react-native-fs#background-downloads-tutorial-ios)部分。
(仅限iOS):如果提供了options.resumable,则将在下载停止时调用它,并且可以使用resumedownload()继续。
b.停止文件下载
stopDownload(jobId: number): void
通过job ID中断下载的文件 ,已下载的文件会保留在文件系统中;
c.重启继续下载
(仅限iOS) resumeDownload(jobId: number): void
通过job ID重启文件下载;
d.检测是否重启继续下载中
(iOS only) resumeDownload(jobId: number): void
检查具有此ID的下载作业是否可以使用resumeDownload()恢复。
if (await RNFS.isResumable(jobId) {
RNFS.resumeDownload(jobId)
}
e.监听文件是否下载完成
(iOS only) completeHandlerIOS(jobId: number): void
要在使用后台下载时使用,请告诉iOS您已完成对已完成下载的处理。
参考:
https://github.com/itinance/react-native-fs#background-downloads-tutorial-ios