iOS文件读写与预览

参考文章:

沙盒(sandbox)(官方文档

  • iOS沙盒主要包含以下三个文件夹:

    image
  • 获取沙盒路径
1
2
//沙盒根目录
NSString *homeDir = NSHomeDirectory();

注:每次编译代码会生成新的沙盒,注意是编译不是启动,所以模拟器或者真机运行你每次运行所得到的沙盒路径都是不一样,就是上面提到的标识符不一样,正式版app真机的话启动杀死,不会生成新的沙盒

1)、Documents

保存应用程序运行时生成的需要持久化数据,iTunes会自动备份该目录。

苹果公司建议将程序中建立的活在程序浏览到的文件数据保存在该目录下,iTunes备份和恢复的时候会包括此目录。

1
2
// 获取Documents目录
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

2)、Library

存储程序的默认设置和其他状态信息,iTunes会自动备份该文件目录

  • Library/Caches:存放缓存文件,iTunes不会备份此目录,此目录下文件不会在应用退出时删除,通常存放体积比较大,但并不是很重要的资源
  • Library/Preferences:保存应用的所有偏好设置,iOS的Setting(设置)应用会在该目录中查找应用的设置信息,iTunes会自动备份该目录。

    ——PS:如果你想对偏好设置进行相应的操作,应该使用NSUserDefaults类来取得和设置应用程序的偏好,而不是直接创建偏好设置文件。
  • 包含两个文件夹

    image
1
2
3
4
5
6
7
8
9
// 获取Library的目录路径
NSString *libDir = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];

// 获取Library/Caches目录
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];

// 获取Library/Preferences目录
NSString *prePath = [NSSearchPathForDirectoriesInDomains(NSPreferencePanesDirectory, NSUserDomainMask, YES) lastObject];
//通常情况下,Preferences由系统维护,我们很少去操作TA

3)、tmp

保存应用程序运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除,应用没有运行时,系统也有可能会清除该目录下的文件,iTunes不会同步该目录,你的iPhone重启时,该目录下的文件会被删除。

数据存储的几种方式

1)、property list(属性列表)

只能存储系统自带的数据类型,一般实际开发中存储字典、数组,自定义的模型无法进行存储。

1
2
3
4
5
6
7
8
9
#define kUserPath [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"local.plist"]

//iOS写入property list
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
[dic setObject:@"小明" forKey:@"name"];
[dic writeToFile:kUserPath atomically:YES];

NSMutableDictionary *localDic = [[NSMutableDictionary alloc] initWithContentsOfFile:kUserPath];
NSLog(@"localDic = %@",localDic);

2)、Preference(偏好设置):NSUserDefaults

只能存储系统自带的数据类型,自定义的对象无法存储。

偏好设置好处:

1.不需要关心文件名(不需要设置路径)

2.键值对存储(账号相关信息) 对象存储

底层实现原理就是封装了一个字典

1
2
3
4
5
6
7
8
9
// Preference(偏好设置)
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
//放到缓存里,并不会马上放到文件里面
[userDefaults setObject:@"123" forKey:@"account"]; //对象
[userDefaults setObject:@"123456" forKey:@"pwd"];
//BOOL类型
[userDefaults setBool:YES forKey:@"status"];
//在ios7 默认不会马上跟硬盘同步 同步操作 起到立即存储的作用
[userDefaults synchronize];

3)、文件读写

3.1 简单对象写入文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
    //字符串写入文件
//获取document路径
NSArray *array = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPathStr = [array lastObject];
//构造字符串文件的存储路径
NSString *strPath = [docPathStr stringByAppendingPathComponent:@"/text.txt"];
//构造字符串对象
NSString *contant = @"Hello, world!";
//将字符串写入到文件
[contant writeToFile:strPath atomically:YES encoding:NSUTF8StringEncoding error:nil];
//数组写入文件
NSString *arrayPath = [docPathStr stringByAppendingString:@"/arry.txt"];
NSArray *arr = @[ @"Jobs", @"Cook", @"Apple" ];
[arr writeToFile:arrayPath atomically:YES];
//字典写入文件
NSString *dicPath = [docPathStr stringByAppendingString:@"/dic.txt"];
NSDictionary *dic = @{ @"name" : @"Jobs", @"sex" : @"男" };
[dic writeToFile:dicPath atomically:YES];
//Data写入文件
UIImage *img = [UIImage imageNamed:@"avatar1"];
NSString *dataPath = [docPathStr stringByAppendingPathComponent:@"data.txt"];
NSData *data = UIImagePNGRepresentation(img);
[data writeToFile:dataPath atomically:YES];

#pragma mark - NSFileManager
NSError *error;
//文件管理,创建单例对象
NSFileManager *manger = [NSFileManager defaultManager];
//创建文件夹
NSString *filePath = [docPathStr stringByAppendingString:@"/test1"];
[manger createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:nil];
//可以创建,移动,复制,删除文件
//创建文件
NSString *filePath1 = [filePath stringByAppendingString:@"/test.png"];
[manger createFileAtPath:filePath1 contents:data attributes:nil];
// 复制文件
NSString *filePath2 = [docPathStr stringByAppendingString:@"/test1/test2.png"];
[manger copyItemAtPath:filePath1 toPath:filePath2 error:&error];
//移动
NSString *filePath3 = [docPathStr stringByAppendingString:@"/test2"];
[manger createDirectoryAtPath:filePath3 withIntermediateDirectories:YES attributes:nil error:&error];
NSString *filePath4 = [filePath3 stringByAppendingString:@"/test4.png"];
[manger moveItemAtPath:filePath1 toPath:filePath4 error:&error];
//删除
[manger removeItemAtPath:filePath4 error:&error];
//判断文件是否存在
BOOL isExist = [manger fileExistsAtPath:filePath4];
NSLog(@"isExist = %d",isExist);

// 获取目录列里所有文件名
NSArray *fileArr = [manger subpathsOfDirectoryAtPath: docPathStr error:nil];
NSLog(@"fileArr = %@",fileArr);
NSArray *fileArr2 = [manger subpathsAtPath: docPathStr ];
NSLog(@"fileArr2 = %@",fileArr2);

3.2、NSKeyedArchiver(归档)+ 文件读写

该方式一般都是保存自定义对象的时候使用.因为plist文件不能够保存自定义对象.

如果一个字典当中保存有自定义对象,如果把这个字典写入到文件当中,它是不会生成plist文件的.

  • 复杂对象写入文件的过程(复杂对象->归档->NSData->writeToFile)
  • 从文件中读取出复杂对象过程(读取文件->NSData->反归档->复杂对象)

    1. 首先,复杂对象所属的类要遵守
    1. 其次,实现协议中的两个方法:

      • -(void)encodeWithCoder:(NSCoder *)aCoder; //序列化
      • -(id)initWithCoder:(NSCoder *)aDecoder; //反序列化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@interface UserModel : NSObject <NSCoding>

@property (nonatomic, assign) int ID;
@property(nonatomic,copy)NSString *name;
@property(nonatomic,copy)NSString *pwd;
@property(nonatomic,assign)int age;

@end

- (instancetype)initWithCoder:(NSCoder *)aDecoder{
self = [super init];
if (self) {
self.ID = [aDecoder decodeIntForKey:@"ID"];
self.name = [aDecoder decodeObjectForKey:@"name"];
self.pwd = [aDecoder decodeObjectForKey:@"pwd"];
self.age = [aDecoder decodeIntForKey:@"age"];
}
return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeInt:self.ID forKey:@"ID"];
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeObject:self.pwd forKey:@"pwd"];
[aCoder encodeInt:self.age forKey:@"age"];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
UserModel *userModel = [[UserModel alloc] init];
userModel.ID = 10;
userModel.name = @"Jobs";
userModel.pwd = @"123456";
userModel.age = 26;

//方法一:
// //归档保存数据
// NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
// NSString *path=[documentPath stringByAppendingPathComponent:@"UserInfo.plist"];
// [NSKeyedArchiver archiveRootObject:userModel toFile:path];
//
// // 反归档
// UserModel *localUser = [NSKeyedUnarchiver unarchiveObjectWithFile:path];

//方法二:
// 准备一个NSMutableData, 用于保存归档后的对象
NSMutableData *data = [NSMutableData data];
// 创建归档工具
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
// 归档
[archiver encodeObject:userModel forKey:@"user"];
// 结束
[archiver finishEncoding];
NSString *mulDataPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *dataPath = [mulDataPath stringByAppendingString:@"/archiver.plist"];
[data writeToFile:dataPath atomically:YES];

//将对象反归档
// 1.从文件中读取二进制数据
NSData *readData = [NSData dataWithContentsOfFile:dataPath];
// 2.定义反归档工具类
NSKeyedUnarchiver *unAchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:readData];
// 3.对对象进行解码
UserModel *localUser1 = [unAchiver decodeObjectForKey:@"user"];
// 4.完成反归档
[unAchiver finishDecoding];

注:归档不是数据持久化手段,而是为文件读写做准备。文件读写才是数据持久化的方式。

Office等文件预览

方法一:UIDocumentInteractionController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//遵循代理方法`<UIDocumentInteractionControllerDelegate>`
- (void)readDocWithDucument1 {
NSString *ducumentLocation = [[NSBundle mainBundle] pathForResource:docNameString ofType:docTypeString];
NSURL *url = [NSURL fileURLWithPath:ducumentLocation];



// copy 到沙盒的cache 路径下,否则不能用第三方app 打开
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
cachePath = [cachePath stringByAppendingPathComponent:@"temp.docx"];
NSURL *cacheUrl = [NSURL fileURLWithPath:cachePath];

NSFileManager *fileManger = [NSFileManager defaultManager];
NSError *error = nil;
[fileManger copyItemAtURL:url toURL:cacheUrl error:&error];


_documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:cacheUrl];
_documentInteractionController.delegate = self;
// [_documentInteractionController presentPreviewAnimated:YES];
[_documentInteractionController presentOptionsMenuFromRect:CGRectZero inView:self.view animated:YES];
}
- (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller {
return self;
}
- (UIView *)documentInteractionControllerViewForPreview:(UIDocumentInteractionController *)controller {
return self.view;
}
- (CGRect)documentInteractionControllerRectForPreview:(UIDocumentInteractionController *)controller {
return self.view.bounds;
}

方法二:QLPreviewController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (void)quickLook {
QLPreviewController *preVC = [[QLPreviewController alloc] init];
preVC.dataSource = self;
[self presentViewController:preVC animated:YES completion:nil];
}
#pragma mark - QLPreviewControllerDataSource 代理方法
- (NSInteger)numberOfPreviewItemsInPreviewController:(QLPreviewController *)controller {
return 1;
}

- (id<QLPreviewItem>)previewController:(QLPreviewController *)controller previewItemAtIndex:(NSInteger)index {
NSString *ducumentLocation = [[NSBundle mainBundle] pathForResource:docNameString ofType:docTypeString];
NSURL *url = [NSURL fileURLWithPath:ducumentLocation];
return url;
}

方法三:UIWebView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (void)readDocfile {
NSString *ducumentLocation = [[NSBundle mainBundle] pathForResource:docNameString ofType:docTypeString];
NSURL *url = [NSURL fileURLWithPath:ducumentLocation];

UIWebView *webView = [[UIWebView alloc] initWithFrame:self.view.frame];
webView.delegate = self;
webView.multipleTouchEnabled = YES;
webView.scalesPageToFit = YES;

NSURLRequest *request = [NSURLRequest requestWithURL:url];
[webView loadRequest:request];

[self.view addSubview:webView];
}