JKSwiftExtension,测试用例在 FileManagerExtensionViewController 里面
目录:
- 1、沙盒路径的获取
- 获取Home的完整路径名
- 获取Documnets的完整路径名
- 获取Library的完整路径名
- 获取/Library/Cache的完整路径名
- 获取Library/Preferences的完整路径名
- 获取Tmp的完整路径名
- 2、文件以及文件夹的操作 扩展
- 创建文件夹(蓝色的,文件夹和文件是不一样的)
- 删除文件夹
- 创建文件
- 删除文件
- 读取文件内容
- 把文字,图片,数组,字典写入文件
- 从文件 读取 文字,图片,数组,字典
- 拷贝(文件夹/文件)的内容 到另外一个(文件夹/文件),新的(文件夹/文件)如果存在就先删除再 拷贝
- 移动(文件夹/文件)的内容 到另外一个(文件夹/文件),新的(文件夹/文件)如果存在就先删除再 移动
- 判断 (文件夹/文件) 是否存在
- 获取 (文件夹/文件) 的前一个路径
- 判断目录是否可读
- 判断目录是否可写
- 根据文件路径获取文件扩展类型
- 根据文件路径获取文件名称,是否需要后缀
- 对指定路径执行浅搜索,返回指定目录路径下的文件、子目录及符号链接的列表(只寻找一层)
- 深度遍历,会递归遍历子文件夹(包括符号链接,所以要求性能的话用enumeratorAtPath)
- 深度遍历,会递归遍历子文件夹(但不会递归符号链接)
- 计算单个 (文件夹/文件) 的大小,单位为字节 (没有进行转换的)
- 计算 (文件夹/文件) 的大小(转换过的)
- 获取(文件夹/文件)属性集合
一、沙盒路径的获取
// MARK:- 一、沙盒路径的获取
/*
- 1、Home(应用程序包)目录
- 整个应用程序各文档所在的目录,包含了所有的资源文件和可执行文件
- 2、Documents
- 保存应用运行时生成的需要持久化的数据,iTunes同步设备时会备份该目录
- 需要保存由"应用程序本身"产生的文件或者数据,例如: 游戏进度,涂鸦软件的绘图
- 目录中的文件会被自动保存在 iCloud
- 注意: 不要保存从网络上下载的文件,否则会无法上架!
- 3、Library
- 3.1、Library/Cache
- 保存应用运行时生成的需要持久化的数据,iTunes同步设备时不备份该目录。一般存放体积大、不需要备份的非重要数据
- 保存临时文件,"后续需要使用",例如: 缓存的图片,离线数据(地图数据)
- 系统不会清理 cache 目录中的文件
- 就要求程序开发时, "必须提供 cache 目录的清理解决方案"
- 3.2、Library/Preference
- 保存应用的所有偏好设置,IOS的Settings应用会在该目录中查找应用的设置信息。iTunes
- 用户偏好,使用 NSUserDefault 直接读写!
- 如果想要数据及时写入硬盘,还需要调用一个同步方法
- 4、tmp
- 保存临时文件,"后续不需要使用"
- tmp 目录中的文件,系统会自动被清空
- 重新启动手机, tmp 目录会被清空
- 系统磁盘空间不足时,系统也会自动清理
- 保存应用运行时所需要的临时数据,使用完毕后再将相应的文件从该目录删除。应用没有运行,系统也可能会清除该目录下的文件,iTunes不会同步备份该目录
*/
public extension FileManager {
// MARK: 1.1、获取Home的完整路径名
/// 获取Home的完整路径名
/// - Returns: Home的完整路径名
static func homeDirectory() -> String {
//获取程序的Home目录
let homeDirectory = NSHomeDirectory()
return homeDirectory
}
// MARK: 1.2、获取Documnets的完整路径名
/// 获取Documnets的完整路径名
/// - Returns: Documnets的完整路径名
static func DocumnetsDirectory() -> String {
//获取程序的documentPaths目录
//方法1
// let documentPaths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
// let documnetPath = documentPaths[0]
//方法2
let ducumentPath = NSHomeDirectory() + "/Documents"
return ducumentPath
}
// MARK: 1.3、获取Library的完整路径名
/**
这个目录下有两个子目录:Caches 和 Preferences
Library/Preferences目录,包含应用程序的偏好设置文件。不应该直接创建偏好设置文件,而是应该使用NSUserDefaults类来取得和设置应用程序的偏好。
Library/Caches目录,主要存放缓存文件,iTunes不会备份此目录,此目录下文件不会再应用退出时删除
*/
/// 获取Library的完整路径名
/// - Returns: Library的完整路径名
static func LibraryDirectory() -> String {
//获取程序的documentPaths目录
//Library目录-方法1
// let libraryPaths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.libraryDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
// let libraryPath = libraryPaths[0]
//
// Library目录-方法2
let libraryPath = NSHomeDirectory() + "/Library"
return libraryPath
}
// MARK: 1.4、获取/Library/Caches的完整路径名
/// 获取/Library/Caches的完整路径名
/// - Returns: /Library/Caches的完整路径名
static func CachesDirectory() -> String {
//获取程序的/Library/Caches目录
let cachesPath = NSHomeDirectory() + "/Library/Caches"
return cachesPath
}
// MARK: 1.5、获取Library/Preferences的完整路径名
/// 获取Library/Preferences的完整路径名
/// - Returns: Library/Preferences的完整路径名
static func PreferencesDirectory() -> String {
//Library/Preferences目录-方法2
let preferencesPath = NSHomeDirectory() + "/Library/Preferences"
return preferencesPath
}
// MARK: 1.6、获取Tmp的完整路径名
/// 获取Tmp的完整路径名,用于存放临时文件,保存应用程序再次启动过程中不需要的信息,重启后清空
/// - Returns: Tmp的完整路径名
static func TmpDirectory() -> String {
//方法1
//let tmpDir = NSTemporaryDirectory()
//方法2
let tmpDir = NSHomeDirectory() + "/tmp"
return tmpDir
}
}
二、文件以及文件夹的操作 扩展
// MARK:- 二、文件以及文件夹的操作 扩展
public extension FileManager {
// MARK: 文件写入的类型
/// 文件写入的类型
enum FileWriteType {
case TextType
case ImageType
case ArrayType
case DictionaryType
}
// MARK: 移动或者拷贝的类型
/// 移动或者拷贝的类型
enum MoveOrCopyType {
case file
case directory
}
/// 文件管理器
static var fileManager: FileManager {
return FileManager.default
}
// MARK: 2.1、创建文件夹(蓝色的,文件夹和文件是不一样的)
/// 创建文件夹(蓝色的,文件夹和文件是不一样的)
/// - Parameter folderName: 文件夹的名字
/// - Returns: 返回创建的 创建文件夹路径
@discardableResult
static func createFolder(folderPath: String) -> (isSuccess: Bool, error: String) {
if !judgeFileOrFolderExists(filePath: folderPath) {
return (true, "")
}
// 不存在的路径才会创建
do {
// withIntermediateDirectories为ture表示路径中间如果有不存在的文件夹都会创建
try fileManager.createDirectory(atPath: folderPath, withIntermediateDirectories: true, attributes: nil)
return (true, "")
} catch _ {
return (false, "创建失败")
}
}
// MARK: 2.2、删除文件夹
/// 删除文件夹
/// - Parameter folderPath: 文件的路径
@discardableResult
static func removefolder(folderPath: String) -> (isSuccess: Bool, error: String) {
let filePath = "\(folderPath)"
guard judgeFileOrFolderExists(filePath: filePath) else {
// 不存在就不做什么操作了
return (true, "")
}
// 文件存在进行删除
do {
try fileManager.removeItem(atPath: filePath)
return (true, "")
} catch _ {
return (false, "删除失败")
}
}
// MARK: 2.3、创建文件
/// 创建文件
/// - Parameter filePath: 文件路径
/// - Returns: 返回创建的结果 和 路径
@discardableResult
static func createFile(filePath: String) -> (isSuccess: Bool, error: String) {
guard judgeFileOrFolderExists(filePath: filePath) else {
// 不存在的文件路径才会创建
// withIntermediateDirectories 为 ture 表示路径中间如果有不存在的文件夹都会创建
let createSuccess = fileManager.createFile(atPath: filePath, contents: nil, attributes: nil)
return (createSuccess, "")
}
return (true, "")
}
// MARK: 2.4、删除文件
/// 删除文件
/// - Parameter filePath: 文件路径
@discardableResult
static func removefile(filePath: String) -> (isSuccess: Bool, error: String) {
guard judgeFileOrFolderExists(filePath: filePath) else {
// 不存在的文件路径就不需要要移除
return (true, "")
}
// 移除文件
do {
try fileManager.removeItem(atPath: filePath)
return (true, "")
} catch _ {
return (false, "移除文件失败")
}
}
// MARK: 2.5、读取文件内容
/// 读取文件内容
/// - Parameter filePath: 文件路径
/// - Returns: 文件内容
@discardableResult
static func readfile(filePath: String) -> String? {
guard judgeFileOrFolderExists(filePath: filePath) else {
// 不存在的文件路径就不需要要移除
return nil
}
let data = fileManager.contents(atPath: filePath)
return String(data: data!, encoding: String.Encoding.utf8)
}
// MARK: 2.6、把文字,图片,数组,字典写入文件
/// 把文字,图片,数组,字典写入文件
/// - Parameters:
/// - writeType: 写入类型
/// - content: 写入内容
/// - writePath: 写入路径
/// - Returns: 写入的结果
@discardableResult
static func writeToFile(writeType: FileWriteType, content: Any, writePath: String) -> (isSuccess: Bool, error: String) {
guard judgeFileOrFolderExists(filePath: writePath) else {
// 不存在的文件路径
return (false, "不存在的文件路径")
}
// 1、文字,2、图片,3、数组,4、字典写入文件
switch writeType {
case .TextType:
let info = "\(content)"
do {
try info.write(toFile: writePath, atomically: true, encoding: String.Encoding.utf8)
return (true, "")
} catch _ {
return (false, "写入失败")
}
case .ImageType:
let data = content as! Data
do {
try data.write(to: URL(fileURLWithPath: writePath))
return (true, "")
} catch _ {
return (false, "写入失败")
}
case .ArrayType:
let array = content as! NSArray
let result = array.write(toFile: writePath, atomically: true)
if result {
return (true, "")
} else {
return (false, "写入失败")
}
case .DictionaryType:
let result = (content as! NSDictionary).write(toFile: writePath, atomically: true)
if result {
return (true, "")
} else {
return (false, "写入失败")
}
}
}
// MARK: 2.7、从文件 读取 文字,图片,数组,字典
/// 从文件 读取 文字,图片,数组,字典
/// - Parameters:
/// - readType: 读取的类型
/// - readPath: 读取文件路径
/// - Returns: 返回读取的内容
@discardableResult
static func readFromFile(readType: FileWriteType, readPath: String) -> (isSuccess: Bool, content: Any?, error: String) {
guard judgeFileOrFolderExists(filePath: readPath), let readHandler = FileHandle(forReadingAtPath: readPath) else {
// 不存在的文件路径
return (false, nil, "不存在的文件路径")
}
let data = readHandler.readDataToEndOfFile()
// 1、文字,2、图片,3、数组,4、字典
switch readType {
case .TextType:
let readString = String(data: data, encoding: String.Encoding.utf8)
return (true, readString, "")
case .ImageType:
let image = UIImage(data: data)
return (true, image, "")
case .ArrayType:
guard let readString = String(data: data, encoding: String.Encoding.utf8) else {
return (false, nil, "读取内容失败")
}
return (true, readString.jsonStringToArray(), "")
case .DictionaryType:
guard let readString = String(data: data, encoding: String.Encoding.utf8) else {
return (false, nil, "读取内容失败")
}
return (true, readString.jsonStringToDictionary(), "")
}
}
// MARK: 2.8、拷贝(文件夹/文件)的内容 到另外一个(文件夹/文件),新的(文件夹/文件)如果存在就先删除再 拷贝
/**
几个小注意点:
1、目标路径,要带上文件夹名称,而不能只写父路径
2、如果是覆盖拷贝,就是说目标路径已存在此文件夹,我们必须先删除,否则提示make directory error(当然这里最好做一个容错处理,比如拷贝前先转移到其他路径,如果失败,再拿回来)
*/
/// 拷贝(文件夹/文件)的内容 到另外一个(文件夹/文件),新的(文件夹/文件)如果存在就先删除再 拷贝
/// - Parameters:
/// - fromeFile: 拷贝的(文件夹/文件)路径
/// - toFile: 拷贝后的(文件夹/文件)路径
/// - isOverwrite: 当要拷贝到的(文件夹/文件)路径存在,会拷贝失败,这里传入是否覆盖
/// - Returns: 拷贝的结果
@discardableResult
static func copyFile(type: MoveOrCopyType, fromeFilePath: String, toFilePath: String, isOverwrite: Bool = true) -> (isSuccess: Bool, error: String) {
// 1、先判断被拷贝路径是否存在
guard judgeFileOrFolderExists(filePath: fromeFilePath) else {
return (false, "被拷贝的(文件夹/文件)路径不存在")
}
// 2、判断拷贝后的文件路径的前一个文件夹路径是否存在,不存在就进行创建
let toFileFolderPath = directoryAtPath(path: toFilePath)
if !judgeFileOrFolderExists(filePath: toFileFolderPath), type == .file ? !createFile(filePath: toFilePath).isSuccess : !createFolder(folderPath: toFileFolderPath).isSuccess {
return (false, "拷贝后路径前一个文件夹不存在")
}
// 3、如果被拷贝的(文件夹/文件)已存在,先删除,否则拷贝不了
if isOverwrite, judgeFileOrFolderExists(filePath: toFilePath) {
do {
try fileManager.removeItem(atPath: toFilePath)
} catch _ {
return (false, "拷贝失败")
}
}
// 4、拷贝(文件夹/文件)
do {
try fileManager.copyItem(atPath: fromeFilePath, toPath: toFilePath)
} catch _ {
return (false, "拷贝失败")
}
return (true, "success")
}
// MARK: 2.9、移动(文件夹/文件)的内容 到另外一个(文件夹/文件),新的(文件夹/文件)如果存在就先删除再 移动
/// 移动(文件夹/文件)的内容 到另外一个(文件夹/文件),新的(文件夹/文件)如果存在就先删除再 移动
/// - Parameters:
/// - fromeFile: 被移动的文件路径
/// - toFile: 移动后的文件路径
@discardableResult
static func moveFile(type: MoveOrCopyType, fromeFilePath: String, toFilePath: String, isOverwrite: Bool = true) -> (isSuccess: Bool, error: String) {
// 1、先判断被拷贝路径是否存在
guard judgeFileOrFolderExists(filePath: fromeFilePath) else {
return (false, "被移动的(文件夹/文件)路径不存在")
}
// 2、判断拷贝后的文件路径的前一个文件夹路径是否存在,不存在就进行创建
let toFileFolderPath = directoryAtPath(path: toFilePath)
if !judgeFileOrFolderExists(filePath: toFileFolderPath), type == .file ? !createFile(filePath: toFilePath).isSuccess : !createFolder(folderPath: toFileFolderPath).isSuccess {
return (false, "移动后路径前一个文件夹不存在")
}
// 3、如果被移动的(文件夹/文件)已存在,先删除,否则拷贝不了
if isOverwrite, judgeFileOrFolderExists(filePath: toFilePath) {
do {
try fileManager.removeItem(atPath: toFilePath)
} catch _ {
return (false, "移动失败")
}
}
// 4、移动(文件夹/文件)
do {
try fileManager.moveItem(atPath: fromeFilePath, toPath: toFilePath)
} catch _ {
return (false, "移动失败")
}
return (true, "success")
}
// MARK: 2.10、判断 (文件夹/文件) 是否存在
/** 判断文件或文件夹是否存在*/
static func judgeFileOrFolderExists(filePath: String) -> Bool {
let exist = fileManager.fileExists(atPath: filePath)
// 查看文件夹是否存在,如果存在就直接读取,不存在就直接反空
guard exist else {
return false
}
return true
}
// MARK: 2.11、获取 (文件夹/文件) 的前一个路径
/// 获取 (文件夹/文件) 的前一个路径
/// - Parameter path: (文件夹/文件) 的路径
/// - Returns: (文件夹/文件) 的前一个路径
static func directoryAtPath(path: String) -> String {
return (path as NSString).deletingLastPathComponent
}
// MARK: 2.12、判断目录是否可读
static func judegeIsReadableFile(path: String) -> Bool {
return fileManager.isReadableFile(atPath: path)
}
// MARK: 2.13、判断目录是否可写
static func judegeIsWritableFile(path: String) -> Bool {
return fileManager.isReadableFile(atPath: path)
}
// MARK: 2.14、根据文件路径获取文件扩展类型
/// 根据文件路径获取文件扩展类型
/// - Parameter path: 文件路径
/// - Returns: 文件扩展类型
static func fileSuffixAtPath(path: String) -> String {
return (path as NSString).pathExtension
}
// MARK: 2.15、根据文件路径获取文件名称,是否需要后缀
/// 根据文件路径获取文件名称,是否需要后缀
/// - Parameters:
/// - path: 文件路径
/// - suffix: 是否需要后缀,默认需要
/// - Returns: 文件名称
static func fileName(path: String, suffix: Bool = true) -> String {
let fileName = (path as NSString).lastPathComponent
guard suffix else {
// 删除后缀
return (fileName as NSString).deletingPathExtension
}
return fileName
}
// MARK: 2.16、对指定路径执行浅搜索,返回指定目录路径下的文件、子目录及符号链接的列表(只寻找一层)
/// 对指定路径执行浅搜索,返回指定目录路径下的文件、子目录及符号链接的列表(只寻找一层)
/// - Parameter folderPath: 建搜索的lujing
/// - Returns: 指定目录路径下的文件、子目录及符号链接的列表
static func shallowSearchAllFiles(folderPath: String) -> Array? {
do {
let contentsOfDirectoryArray = try fileManager.contentsOfDirectory(atPath: folderPath)
return contentsOfDirectoryArray
} catch _ {
return nil
}
}
// MARK: 2.17、深度遍历,会递归遍历子文件夹(包括符号链接,所以要求性能的话用enumeratorAtPath)
/**深度遍历,会递归遍历子文件夹(包括符号链接,所以要求性能的话用enumeratorAtPath)*/
static func getAllFileNames(folderPath: String) -> Array? {
// 查看文件夹是否存在,如果存在就直接读取,不存在就直接反空
if (judgeFileOrFolderExists(filePath: folderPath)) {
guard let subPaths = fileManager.subpaths(atPath: folderPath) else {
return nil
}
return subPaths
} else {
return nil
}
}
// MARK: 2.18、深度遍历,会递归遍历子文件夹(但不会递归符号链接)
/** 对指定路径深度遍历,会递归遍历子文件夹(但不会递归符号链接))*/
static func deepSearchAllFiles(folderPath: String) -> Array? {
// 查看文件夹是否存在,如果存在就直接读取,不存在就直接反空
if (judgeFileOrFolderExists(filePath: folderPath)) {
guard let contentsOfPathArray = fileManager.enumerator(atPath: folderPath) else {
return nil
}
return contentsOfPathArray.allObjects
}else{
return nil
}
}
// MARK: 2.19、计算单个 (文件夹/文件) 的大小,单位为字节(bytes) (没有进行转换的)
/// 计算单个 (文件夹/文件) 的大小,单位为字节 (没有进行转换的)
/// - Parameter filePath: (文件夹/文件) 路径
/// - Returns: 单个文件或文件夹的大小
static func fileOrDirectorySingleSize(filePath: String) -> UInt64 {
// 1、先判断文件路径是否存在
guard judgeFileOrFolderExists(filePath: filePath) else {
return 0
}
// 2、读取文件大小
do {
let fileAttributes = try fileManager.attributesOfItem(atPath: filePath)
guard let fileSizeValue = fileAttributes[FileAttributeKey.size] as? UInt64 else {
return 0
}
return fileSizeValue
} catch {
return 0
}
}
//MARK: 2.20、计算 (文件夹/文件) 的大小(转换过的)
/// 计算 (文件夹/文件) 的大小
/// - Parameter path: (文件夹/文件) 的路径
/// - Returns: (文件夹/文件) 的大小
static func fileOrDirectorySize(path: String) -> String {
if path.count == 0, !fileManager.fileExists(atPath: path) {
return "0MB"
}
// (文件夹/文件) 的实际大小
var fileSize: UInt64 = 0
do {
let files = try fileManager.contentsOfDirectory(atPath: path)
for file in files {
let path = path + "/\(file)"
fileSize = fileSize + fileOrDirectorySingleSize(filePath: path)
}
} catch {
fileSize = fileSize + fileOrDirectorySingleSize(filePath: path)
}
// 转换后的大小 ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
return covertUInt64ToString(with: fileSize)
}
// MARK: 2.21、获取(文件夹/文件)属性集合
/// 获取(文件夹/文件)属性集合
/// - Parameter path: (文件夹/文件)路径
/// - Returns: (文件夹/文件)属性集合
@discardableResult
static func fileAttributes(path: String) -> ([FileAttributeKey : Any]?) {
do {
let attributes = try fileManager.attributesOfItem(atPath: path)
/*
print("创建时间:\(attributes[FileAttributeKey.creationDate]!)")
print("修改时间:\(attributes[FileAttributeKey.modificationDate]!)")
print("文件大小:\(attributes[FileAttributeKey.size]!)")
*/
return attributes
} catch _ {
return nil
}
// key的列表如:
/*
public static let type:
public static let size:
public static let modificationDate:
public static let referenceCount:
public static let deviceIdentifier:
public static let ownerAccountName:
public static let groupOwnerAccountName:
public static let posixPermissions:
public static let systemNumber:
public static let systemFileNumber:
public static let extensionHidden:
public static let hfsCreatorCode:
public static let hfsTypeCode:
public static let immutable:
public static let appendOnly:
public static let creationDate:
public static let ownerAccountID:
public static let groupOwnerAccountID:
public static let busy:
@available(iOS 4.0, *)
public static let protectionKey:
public static let systemSize:
public static let systemFreeSize:
public static let systemNodes:
public static let systemFreeNodes:
*/
}
}
// MARK:- fileprivate
extension FileManager {
// MARK: 计算文件大小:UInt64 -> String
/// 计算文件大小:UInt64 -> String
/// - Parameter size: 文件的大小
/// - Returns: 转换后的文件大小
fileprivate static func covertUInt64ToString(with size: UInt64) -> String {
var convertedValue: Double = Double(size)
var multiplyFactor = 0
let tokens = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
while convertedValue > 1024 {
convertedValue /= 1024
multiplyFactor += 1
}
return String(format: "%4.2f %@", convertedValue, tokens[multiplyFactor])
}
}