logo头像
Snippet 博客主题

Swift数据存储

一、沙盒

image.png

最上面的bundle container目录为APP程序的安装目录,在安装后为不可修改状态。
中间的data container目录为APP数据存储目录,保存APP运行时需要的数据。
最下面的iCloud container目录为云存储目录,当APP需要iCloud云存储时可以进行访问。

####data container文件目录如图
image.png

####Documents
保存用户创建的文档文件的目录,用户可以通过文件分享分享该目录下的文件。在iTunes和iCloud备份时会备份该目录。建议保存你希望用户看得见的文件。

Library

该目录下除Caches目录外,在iTunes和iCloud备份时会备份除Caches目录外的其他所有目录。

  • Cache
    建议保存数据缓存使用。在用户的磁盘空间已经使用完毕时有可能删除该目录下的文件,在APP使用期间不会删除,APP没有运行时系统有可能进行删除。需要持久化的数据建议不要保存在该目录下,以免系统强制删除。
  • Preferences
    用户偏好存储目录,在使用NSUserDefaults或者CFPreferences接口保存的数据保存在该目录下,编程人员不需要对该目录进行管理。在iTunes和iCloud备份时会备份该目录
  • Application Support
    建议用来存储除用户数据相关以外的所有文件,如游戏的新关卡。在iTunes和iCloud备份时会备份该目录。
  • Frameworks
    用来保存动态库的文件夹,在iOS系统中已不能使用,该目录可以忽略

tmp

苹果建议该目录用来保存临时使用的数据,编程人员应该在数据长时间内不使用时主动删除该目录下的文件,在APP没有运行期间,系统可能删除该目录下的文件。在iTunes和iCloud备份时不会备份该目录

综上所述,我们保存文件可以保存在Application Support或者Documents目录之下,临时缓存文件则可根据需要保存到tmp和Cache目录下面,由于tmp和Cache目录有可能被系统删除,所有不能保存重要的文件数据。
除系统推荐的目录之外,用户也可以自己创建目录,也可以在推荐的目录下创建子目录进行管理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//沙盒根目录
NSString *homePath = NSHomeDirectory();
//document目录
NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
//library目录
NSString *libraryPath = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES).firstObject;
//caches目录
NSString *cachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
//application support目录
NSString *applicationSupportPath = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES).firstObject;
//preference目录
NSString *preferencePath = NSSearchPathForDirectoriesInDomains(NSPreferencePanesDirectory, NSUserDomainMask, YES).firstObject;
//tem目录
NSString *temPath = NSTemporaryDirectory();

二、 文件操作

####1. 遍历一个目录下的所有文件

1
2
3
4
let manager = FileManager.default
let urlForDocument = manager.urls(for: .documentDirectory, in:.userDomainMask)
let url = urlForDocument[0] as URL
print(url)
  • 对指定路径执行浅搜索,返回指定目录路径下的文件、子目录及符号链接的列表

    1
    2
    let contentsOfPath = try? manager.contentsOfDirectory(atPath: url.path)
    print("contentsOfPath: \(contentsOfPath)")
  • 对指定路径执行浅搜索,返回指定目录路径下的文件、子目录及符号链接的列表

    1
    2
    3
    let contentsOfURL = try? manager.contentsOfDirectory(at: url,
                            includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
    print("contentsOfURL: \(contentsOfURL)")
  • 深度遍历,会递归遍历子文件夹(但不会递归符号链接

    1
    2
    3
    let enumeratorAtURL = manager.enumerator(at: url, includingPropertiesForKeys: nil,
    options: .skipsHiddenFiles, errorHandler:nil)
    print("enumeratorAtURL: \(enumeratorAtURL?.allObjects)")
  • 深度遍历,会递归遍历子文件夹(包括符号链接,所以要求性能的话用enumeratorAtPath)

    1
    2
    let subPaths = manager.subpaths(atPath: url.path)
    print("subPaths: \(subPaths)")

####2.判断文件或文件夹是否存在

1
2
3
let fileManager = FileManager.default
let filePath:String = NSHomeDirectory() + "/Documents/aaa.txt"
let exist = fileManager.fileExists(atPath: filePath)

3.创建文件夹

方式一

1
2
3
4
5
6
let myDirectory:String = NSHomeDirectory() + "/Documents/myFolder/Files"
let fileManager = FileManager.default

//withIntermediateDirectories为ture表示路径中间如果有不存在的文件夹都会创建
try! fileManager.createDirectory(atPath: myDirectory,
withIntermediateDirectories: true, attributes: nil)

方式二

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func createFolder(name:String,baseUrl:NSURL){
let manager = FileManager.default
let folder = baseUrl.appendingPathComponent(name, isDirectory: true)
print("文件夹: \(folder)")
let exist = manager.fileExists(atPath: folder!.path)
if !exist {
try! manager.createDirectory(at: folder!, withIntermediateDirectories: true,
attributes: nil)
}
}

//在文档目录下新建folder目录
let manager = FileManager.default
let urlForDocument = manager.urls(for: .documentDirectory, in: .userDomainMask)
let url = urlForDocument[0] as NSURL
createFolder(name: "folder", baseUrl: url)

####4.将对象写入文件
可以通过write(to:)方法,可以创建文件并将对象写入,对象包括String,NSString,UIImage,NSArray,NSDictionary等。

  • 把String保存到文件

    1
    2
    3
    let filePath:String = NSHomeDirectory() + "/Documents/aaa.txt"
    let info = "学习使我快乐"
    try! info.write(toFile: filePath, atomically: true, encoding: String.Encoding.utf8)
  • 把图片保存到文件路径下

    1
    2
    3
    4
    let filePath = NSHomeDirectory() + "/Documents/aaa.png"
    let image = UIImage(named: "aaa.png")
    let data:Data = UIImagePNGRepresentation(image!)!
    try? data.write(to: URL(fileURLWithPath: filePath))
  • 把NSArray保存到文件路径下

    1
    2
    3
    let array = NSArray(objects: "aaa","bbb","ccc")
    let filePath:String = NSHomeDirectory() + "/Documents/array.plist"
    array.write(toFile: filePath, atomically: true)
  • 把NSDictionary保存到文件路径下

    1
    2
    3
    let dictionary:NSDictionary = ["Gold": "1st Place", "Silver": "2nd Place"]
    let filePath:String = NSHomeDirectory() + "/Documents/dictionary.plist"
    dictionary.write(toFile: filePath, atomically: true)

####5. 创建文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func createFile(name:String, fileBaseUrl:URL){
let manager = FileManager.default

let file = fileBaseUrl.appendingPathComponent(name)
print("文件: \(file)")
let exist = manager.fileExists(atPath: file.path)
if !exist {
let data = Data(base64Encoded:"aGVsbG8gd29ybGQ=" ,options:.ignoreUnknownCharacters)
let createSuccess = manager.createFile(atPath: file.path,contents:data,attributes:nil)
print("文件创建结果: \(createSuccess)")
}
}

//在文档目录下新建test.txt文件
let manager = FileManager.default
let urlForDocument = manager.urls( for: .documentDirectory,
in:.userDomainMask)
let url = urlForDocument[0]
createFile(name:"test.txt", fileBaseUrl: url)

####6.复制文件
方法一

1
2
3
4
5
let fileManager = FileManager.default
let homeDirectory = NSHomeDirectory()
let srcUrl = homeDirectory + "/Documents/aaa.txt"
let toUrl = homeDirectory + "/Documents/copyed.txt"
try! fileManager.copyItem(atPath: srcUrl, toPath: toUrl)

方法二

1
2
3
4
5
6
7
8
9
10
// 定位到用户文档目录
let manager = FileManager.default
let urlForDocument = manager.urls( for:.documentDirectory, in:.userDomainMask)
let url = urlForDocument[0]

// 将test.txt文件拷贝到文档目录根目录下的copyed.txt文件
let srcUrl = url.appendingPathComponent("test.txt")
let toUrl = url.appendingPathComponent("copyed.txt")

try! manager.copyItem(at: srcUrl, to: toUrl)

####7.移动文件
方法一

1
2
3
4
5
let fileManager = FileManager.default
let homeDirectory = NSHomeDirectory()
let srcUrl = homeDirectory + "/Documents/aaa.txt"
let toUrl = homeDirectory + "/Documents/moved/aaa.txt"
try! fileManager.moveItem(atPath: srcUrl, toPath: toUrl)

方法二

1
2
3
4
5
6
7
8
9
// 定位到用户文档目录
let manager = FileManager.default
let urlForDocument = manager.urls( for: .documentDirectory, in:.userDomainMask)
let url = urlForDocument[0]

let srcUrl = url.appendingPathComponent("test.txt")
let toUrl = url.appendingPathComponent("copyed.txt")
// 移动srcUrl中的文件(test.txt)到toUrl中(copyed.txt)
try! manager.moveItem(at: srcUrl, to: toUrl)

8.删除文件

方法一

1
2
3
4
let fileManager = FileManager.default
let homeDirectory = NSHomeDirectory()
let srcUrl = homeDirectory + "/Documents/hangge.txt"
try! fileManager.removeItem(atPath: srcUrl)

方法二

1
2
3
4
5
6
7
8
// 定位到用户文档目录
let manager = FileManager.default
let urlForDocument = manager.urls(for: .documentDirectory, in:.userDomainMask)
let url = urlForDocument[0]

let toUrl = url.appendingPathComponent("copyed.txt")
// 删除文档根目录下的toUrl路径的文件(copyed.txt文件)
try! manager.removeItem(at: toUrl)

####9.删除目录下所有的文件
方法1:获取所有文件,然后遍历删除

1
2
3
4
5
6
let fileManager = FileManager.default
let myDirectory = NSHomeDirectory() + "/Documents/Files"
let fileArray = fileManager.subpaths(atPath: myDirectory)
for fn in fileArray!{
try! fileManager.removeItem(atPath: myDirectory + "/\(fn)")
}

方法2:删除目录后重新创建该目录

1
2
3
4
5
let fileManager = FileManager.default
let myDirectory = NSHomeDirectory() + "/Documents/Files"
try! fileManager.removeItem(atPath: myDirectory)
try! fileManager.createDirectory(atPath: myDirectory, withIntermediateDirectories: true,
attributes: nil)

10.读取文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let manager = FileManager.default
let urlsForDocDirectory = manager.urls(for: .documentDirectory, in:.userDomainMask)
let docPath = urlsForDocDirectory[0]
let file = docPath.appendingPathComponent("test.txt")

//方法1
let readHandler = try! FileHandle(forReadingFrom:file)
let data = readHandler.readDataToEndOfFile()
let readString = String(data: data, encoding: String.Encoding.utf8)
print("文件内容: \(readString)")

//方法2
let data2 = manager.contents(atPath: file.path)
let readString2 = String(data: data2!, encoding: String.Encoding.utf8)
print("文件内容: \(readString2)")

11.在任意位置写入数据

1
2
3
4
5
6
7
8
9
10
let manager = FileManager.default
let urlsForDocDirectory = manager.urls(for:.documentDirectory, in:.userDomainMask)
let docPath = urlsForDocDirectory[0]
let file = docPath.appendingPathComponent("test.txt")

let string = "添加一些文字到文件末尾"
let appendedData = string.data(using: String.Encoding.utf8, allowLossyConversion: true)
let writeHandler = try? FileHandle(forWritingTo:file)
writeHandler!.seekToEndOfFile()
writeHandler!.write(appendedData!)

12.文件权限判断

1
2
3
4
5
6
7
8
9
10
11
12
13
let manager = FileManager.default
let urlForDocument = manager.urls(for: .documentDirectory, in:.userDomainMask)
let docPath = urlForDocument[0]
let file = docPath.appendingPathComponent("test.txt")

let readable = manager.isReadableFile(atPath: file.path)
print("可读: \(readable)")
let writeable = manager.isWritableFile(atPath: file.path)
print("可写: \(writeable)")
let executable = manager.isExecutableFile(atPath: file.path)
print("可执行: \(executable)")
let deleteable = manager.isDeletableFile(atPath: file.path)
print("可删除: \(deleteable)")

####13.获取文件属性(创建时间,修改时间,文件大小,文件类型等信息)

1
2
3
4
5
6
7
8
9
10
let manager = FileManager.default
let urlForDocument = manager.urls(for: .documentDirectory, in:.userDomainMask)
let docPath = urlForDocument[0]
let file = docPath.appendingPathComponent("test.txt")

let attributes = try? manager.attributesOfItem(atPath: file.path) //结果为Dictionary类型
print("attributes: \(attributes!)")
print("创建时间:\(attributes![FileAttributeKey.creationDate]!)")
print("修改时间:\(attributes![FileAttributeKey.modificationDate]!)")
print("文件大小:\(attributes![FileAttributeKey.size]!)")

####14.文件/文件夹比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let manager = FileManager.default
let urlForDocument = manager.urls(for: .documentDirectory, in:.userDomainMask)
let docPath = urlForDocument[0]
let contents = try! manager.contentsOfDirectory(atPath: docPath.path)

//下面比较用户文档中前面两个文件是否内容相同(该方法也可以用来比较目录)
let count = contents.count
if count > 1 {
let path1 = docPath.path + "/" + (contents[0] as String)
let path2 = docPath.path + "/" + (contents[1] as String)
let equal = manager.contentsEqual(atPath: path1,andPath:path2)
print("path1:\(path1)")
print("path2:\(path2)")
print("比较结果: \(equal)")
}

###15.程序打包安装的目录 NSBundle.mainBundle()
工程打包安装后会在NSBundle.mainBundle()路径下,该路径是只读的,不允许修改。
所以当我们工程中有一个SQLite数据库要使用,在程序启动时,我们可以把该路径下的数据库拷贝一份到Documents路径下,以后整个工程都将操作Documents路径下的数据库。

1
2
3
4
5
6
7
8
9
//声明一个Documents下的路径
var dbPath = NSHomeDirectory() + "/Documents/hanggeDB.sqlite"
//判断数据库文件是否存在
if !NSFileManager.defaultManager().fileExistsAtPath(dbPath){
//获取安装包内数据库路径
var bundleDBPath:String? = NSBundle.mainBundle().pathForResource("hanggeDB", ofType: "sqlite")
//将安装包内数据库拷贝到Documents目录下
NSFileManager.defaultManager().copyItemAtPath(bundleDBPath!, toPath: dbPath, error: nil)
}

三、存储方式

微信打赏

赞赏是不耍流氓的鼓励