建议大家看 JKSwiftExtension,测试用例在 FileManagerExtensionViewController ,是关于沙盒的操作
在iOS开发我们会遇到文件、音频、视频等等下载后本地存储的情况,这时对读文件,写文件就显得很重要,对文件夹以及文件中的文件的操作,这时就可以使用NSFileManager(FileManager)或NSFileHandle(FileHandle)来实现。下面会用OC和Swift的对比来实现对文件和文件夹的操作
- 文件管理器(NSFileManager/FileManager):此类主要是对文件进行的操作(创建/删除/改名等)以及文件信息的获取。
- 文件连接器(NSFileHandle/FileHandle):此类主要是对文件内容进行读取和写入操作。
一、沙盒以及组成部分
iOS应用程序只能对自己创建的文件系统读取文件,这个"独立","封闭","安全"的空间,称之为沙盒。
- 1.1、Home目录(应用程序包)
- 整个应用程序各文档所在的目录,包含了所有的资源文件和可执行文件
- 1.2、Documents
- 保存应用运行时生成的需要持久化的数据,iTunes同步设备时会备份该目录
- 需要保存由"应用程序本身"产生的文件或者数据,例如: 游戏进度,涂鸦软件的绘图
- 目录中的文件会被自动保存在 iCloud
- 注意: 不要保存从网络上下载的文件,否则会无法上架!
- 1.3、tmp
- 保存应用运行时所需要的临时数据或文件,"后续不需要使用",使用完毕后再将相应的文件从该目录删除。
- 应用没有运行,系统也可能会清除该目录下的文件
- iTunes不会同步备份该目录
- 重新启动手机, tmp 目录会被清空
- 系统磁盘空间不足时,系统也会自动清理
- 1.4、Library/Cache
- 保存应用运行时生成的需要持久化的数据,iTunes同步设备时不备份该目录。一般存放体积大、不需要备份的非重要数据
- 保存临时文件,"后续需要使用",例如: 缓存的图片,离线数据(地图数据)
- 系统不会清理 cache 目录中的文件
- 就要求程序开发时, "必须提供 cache 目录的清理解决方案"
- 1.5、Library/Preference
- 保存应用的所有偏好设置,IOS的Settings应用会在该目录中查找应用的设置信息。iTunes
- 用户偏好,使用 NSUserDefault 直接读写!
- 如果想要数据及时写入硬盘,还需要调用一个同步方法 synchronize()
- 1.6.程序.app,与另三个路径的父路径不同
- 这是应用程序的程序包目录,包含应用程序的本身。由于应用程序必须经过签名,所以您在运行时不能对这个目录中的内容进行修改,否则可能会使应用程序无法启动
二、对文件以及文件夹的操作
2.1、获取各个目录的路径
- 2.1.1、HomeDirectory
OC: NSString *filePath = NSHomeDirectory(); Swift: let homePath = NSHomeDirectory()
- 2.1.2、Documents
OC: 方法一 NSString * documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES) objectAtIndex:0]; 方法二 NSString * documentsPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]; Swift: 方法1 let documentsPaths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true) let documentsPath = documentPaths[0] 方法2 let documentsPath = NSHomeDirectory()+"/Documents"
- 2.1.3、Caches
OC: 方法一 NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0]; 方法二 NSString *cachesPath= [NSHomeDirectory() stringByAppendingPathComponent:@"/Library/Caches"]; Swift: 方法1 let cachePaths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.cachesDirectory, FileManager.SearchPathDomainMask.userDomainMask, true) let cachePath = cachePaths.last 方法2 let cachePath = NSHomeDirectory()+"/Library/Caches"
- 2.1.4、Library
OC: 方法一 NSString * libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0]; 方法二 NSString * libraryPath = [NSHomeDirectory() stringByAppendingPathComponent:@"/Library"]; Swift: 方法1 let libraryPaths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.libraryDirectory, FileManager.SearchPathDomainMask.userDomainMask, true) let libraryPath = libraryPaths[0] 方法2 let libraryPath = NSHomeDirectory()+"/Library"
- 2.1.5、tmp
OC: 方法一 NSString *tempPath = NSTemporaryDirectory(); 方法二 NSString * tempPath = [NSHomeDirectory() stringByAppendingPathComponent:@"/tmp"]; Swift: 方法1 let tempPath = NSTemporaryDirectory() 方法2 let tempPath = NSHomeDirectory()+"/tmp"
2.2、根据传件来的路径创建文件夹 创建文件目录(蓝色的,文件夹和文件是不一样的)
应用程序目录, Caches、Library、Documents目录文件夹下创建文件夹(蓝色的)
下面以Documents为例创建JKFile为例
- OC
NSString *filePath=[NSHomeDirectory() stringByAppendingPathComponent:@"Documents/JKFile"]; - (NSString *)jKCreateDir:folderName{ NSString *filePath=[NSHomeDirectory() stringByAppendingPathComponent: folderName]; NSFileManager *fileManager = [NSFileManager defaultManager]; BOOL isDir = NO; // fileExistsAtPath 判断一个文件或目录是否有效,isDirectory判断是否一个目录 BOOL existed = [fileManager fileExistsAtPath:filePath isDirectory:&isDir]; if ( !(isDir == YES && existed == YES) ) { // 不存在的路径才会创建 [fileManager createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:nil]; } return filePath; }
- Swift:
let jKFilePath = NSHomeDirectory() + "/Documents/JKFile"; func jKCreateFolder(folderName: NSString) -> NSString { let fileManager: FileManager = FileManager.default let filePath = "\(folderName)" let exist = fileManager.fileExists(atPath: filePath) // 不存在的路径才会创建 if (!exist) { //withIntermediateDirectories为ture表示路径中间如果有不存在的文件夹都会创建 try! fileManager.createDirectory(atPath: filePath,withIntermediateDirectories: true, attributes: nil) } return filePath as NSString }
2.3、删除文件夹(先判断文件夹存不存在)
- OC
NSString *filePath=[NSHomeDirectory() stringByAppendingPathComponent:@"Documents/JKFile"]; - (void)jKRemovefolder:(NSString *)filePathName { // filePath: 文件/目录的路径 NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *filePath = [NSString stringWithFormat:@"%@",filePathName]; BOOL isDir = NO; // fileExistsAtPath 判断一个文件或目录是否有效,isDirectory判断是否一个目录 BOOL existed = [fileManager fileExistsAtPath:filePath isDirectory:&isDir]; if ( !(isDir == YES && existed == YES) ) { // 不存在的路径才会创建 return; } //文件夹 [fileManager removeItemAtPath:filePath error:nil]; }
- Swift:
let jKFilePath = NSHomeDirectory() + "/Documents/JKFile"; func jKRemovefolder(folderName: NSString){ let fileManager: FileManager = FileManager.default let filePath = "\(folderName)" let exist = fileManager.fileExists(atPath: filePath) // 查看文件夹是否存在,如果存在就直接读取,不存在就直接反空 if (exist) { try! fileManager.removeItem(atPath: filePath) }else{ // 不存在就不做什么操作了 } }
2.4、删除文件
- OC
- (void)jKRemovefile:(NSString *)filePathName { // filePath: 文件/目录的路径 NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *filePath = [NSString stringWithFormat:@"%@",filePathName]; //移除文件 [fileManager removeItemAtPath:filePath error:nil]; }
- Swift:
func jKRemovefile(folderName: NSString){ let fileManager: FileManager = FileManager.default let filePath = "\(folderName)" //移除文件 try! fileManager.removeItem(atPath: filePath) }
2.5、深度遍历(搜索文件夹)
- 2.5.1、深度搜索遍历一(subpathsAtPath)深度遍历,会递归遍历子文件夹(包括符号链接,所以要求性能的话用enumeratorAtPath)
- 获取某个文件下的所有文件的名字
- OC
NSString *filePath = NSHomeDirectory(); -(NSArray *)jKGetAllFileNames:(NSString *)folderName { NSFileManager *fileManager = [NSFileManager defaultManager]; // 取得一个目录下得所有文件名 NSArray *files = [fileManager subpathsAtPath:[self jKCreateFolder:folderName]]; //NSLog(@"pdf名字的数量=%ld 数组=%@",files.count,files); return files; }
- Swift:
let jKFilePath = NSHomeDirectory(); func jKGetAllFileNames(folderName: NSString) -> NSArray{ let filePath = "\(folderName)" let exist = fileManager.fileExists(atPath: filePath) // 查看文件夹是否存在,如果存在就直接读取,不存在就直接反空 if (exist) { let subPaths = fileManager.subpaths(atPath: folderName as String) return subPaths! as NSArray }else{ return [] } }
- 2.5.2、深度遍历二,会递归遍历子文件夹(但不会递归符号链接)
- OC
// folderNmae:文件夹的名字 -(NSArray *)jKDeepSearchEnumeratorAllFileNames:(NSString *)folderName{ NSFileManager *fileManager = [NSFileManager defaultManager]; // 取得一个目录下得所有文件名 NSDirectoryEnumerator *files = [fileManager enumeratorAtPath:[self jKCreateFolder:folderName]]; //NSLog(@"pdf名字的数量=%ld 数组=%@",files.count,files); return files.allObjects; }
- Swift:
func jKDeepSearchAllFiles(folderName: NSString) -> NSArray { let filePath = "\(folderName)" let exist = fileManager.fileExists(atPath: filePath) // 查看文件夹是否存在,如果存在就直接读取,不存在就直接反空 if (exist) { let contentsOfPathArray = fileManager.enumerator(atPath: filePath) return contentsOfPathArray!.allObjects as NSArray }else{ return [] } }
2.6、对指定路径执行浅搜索,返回指定目录路径下的文件、子目录及符号链接的列表(只寻找一层)
- OC
/**对指定路径执行浅搜索,返回指定目录路径下的文件、子目录及符号链接的列表(只寻找一层)*/ NSString *customPath = [NSString stringWithFormat:@"%@",[JKFilePathOperationExtension jKHomeDirectory]]; -(NSArray *)jKShallowSearchAllFiles:(NSString *)filePath{ NSFileManager *fileManager = [NSFileManager defaultManager]; NSArray *contentsOfPathArray = [fileManager contentsOfDirectoryAtPath:filePath error:nil]; return contentsOfPathArray; }
- Swift:
/** 对指定路径执行浅搜索,读取指定目录路径下的文件、子目录及符号链接的列表(只寻找一层)*/ let jKFilePath = NSHomeDirectory() func jKShallowSearchAllFiles(folderName: NSString) -> NSArray { let filePath = "\(folderName)" let contentsOfPathArray = try! fileManager.contentsOfDirectory(atPath: filePath); return contentsOfPathArray as NSArray }
2.7、判断文件或文件夹是否存在
- OC
+(BOOL)jkJudgeFileOrFolderExists:(NSString *)filePathName{ NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *filePath = [NSString stringWithFormat:@"%@",filePathName]; BOOL isDir = NO; // fileExistsAtPath 判断一个文件或目录是否有效,isDirectory判断是否一个目录 BOOL existed = [fileManager fileExistsAtPath:filePath isDirectory:&isDir]; if ( !(isDir == YES && existed == YES) ) { // 不存在的路径 return NO; }else{ return YES; } return nil; }
- Swift:
func jkJudgeFileOrFolderExists(folderName: NSString) -> Bool { let filePath = "\(folderName)" let exist = fileManager.fileExists(atPath: filePath) // 查看文件夹是否存在,如果存在就直接读取,不存在就直接反空 if (exist) { return true }else{ return false } }
2.8、创建文件(如:动画乐园.text格式的文本文件)
- OC
/**folderNmae:文件的名字*/ - (NSString *)jKCreateFile:(NSString *)folderName{ NSString *filePath = [NSString stringWithFormat:@"%@",folderName]; NSFileManager *fileManager = [NSFileManager defaultManager]; BOOL isDir = NO; // fileExistsAtPath 判断一个文件或目录是否有效,isDirectory判断是否一个目录 BOOL existed = [fileManager fileExistsAtPath:filePath isDirectory:&isDir]; if ( !(isDir == YES && existed == YES) ) { // 不存在的路径才会创建 [fileManager createFileAtPath:filePath contents:nil attributes:nil]; } return filePath; }
- Swift:
// fileName:文件的名字(不是文件夹) // baseFilePath: 文件的基础路径 // content: 存进文件的内容 /** 根据传件来的路径创建文件*/ func jKCreateFile(fileName: NSString,baseFilePath: NSString) -> (filePath: NSString,createStatus: Bool) { // NSHomeDirectory():应用程序目录 let filePath = "\(baseFilePath)" + "/\(fileName)" let exist = fileManager.fileExists(atPath: filePath) // 不存在的文件路径才会创建 if (!exist) { //withIntermediateDirectories为ture表示路径中间如果有不存在的文件夹都会创建 let createSuccess = fileManager.createFile(atPath: filePath,contents:nil,attributes:nil) return (filePath as NSString,createSuccess as Bool) } return (filePath as NSString,true) }
2.9、可以通过write(to:)方法,可以创建文件并将对象(文件,音频,图片,视频以及数组,字典)都可以写入文件
- 简单对象:iOS中提供四种类型可以直接进行文件存取:NSString(字符串)、NSArray(数组)、NSDictionary(字典)、NSData(数据)(以上类型包括子类)
- 注意:数组(可变与不可变)和字典(可变与不可变)中元素对象的类型,也必须是上述四种,否则不能直接写入文件
- 2.9.1、把NSSString保存到上面“动画乐园.text”的文件里面