Swift需要通过FileManager完成文件系统操作。
删除方式
1、删除整个目录
func deleteDirectory(at url: URL) throws {
try FileManager.default.removeItem(at: url)
}
2、只清空目录内容
func clearDirectoryContents(at url: URL) throws {
let fm = FileManager.default
let items = try fm.contentsOfDirectory(at: url, includingPropertiesForKeys: nil)
for item in items {
try fm.removeItem(at: item)
}
}
contentsOfDirectory方法可以获取目录中的文件列表。
例如:
temp/
├─ a.txt
├─ folder1/
│ └─ x.png
└─ folder2/
└─ y.jpg
contentsOfDirectory得到:
[a.txt, folder1, folder2]
然后调用removeItem方法删除文件和目录中的所有内容。
注意:如果目录内容过大,删除步骤可能会阻塞UI,建议使用后台线程处理删除任务:
func deleteDirectoryAsync(at url: URL) async {
// Task.detached 后台线程删除文件
try await Task.detached(priority: .userInitiated) {
try FileManager.default.removeItem(at: url)
}
}
3、精细化清空目录内容
如果想要统计删除的内容,并且想要过滤隐藏文件、统计删除失败等场景,可以enumerator方法遍历目录中的所有内容并删除。
static func cleanTempFolder() async -> Bool {
let tempURL = FileManager.default.temporaryDirectory
// 遍历目录内容
guard let enumerator = FileManager.default.enumerator(
at: tempURL,
includingPropertiesForKeys: [.fileSizeKey, .isRegularFileKey, .isDirectoryKey],
options: [.skipsHiddenFiles, .skipsPackageDescendants]
) else {
print("无法访问 \(tempURL)")
return false
}
// 异步环境中将 enumerator 转换为 Sendable 的 Array 类型
let allFiles = Array(enumerator)
var filesToDelete: [(url: URL, size: Int)] = []
var dirsToDelete: [URL] = []
for case let fileURL as URL in allFiles {
do {
let values = try fileURL.resourceValues(forKeys: [.fileSizeKey, .isRegularFileKey, .isDirectoryKey])
if values.isRegularFile == true {
let size = values.fileSize ?? 0
filesToDelete.append((url: fileURL,size: size))
} else if values.isDirectory == true {
dirsToDelete.append(fileURL)
}
} catch {
print("读取文件大小失败:\(fileURL),错误: \(error)")
}
}
// 在后台线程删除
return await Task.detached(priority: .userInitiated) {
var deletedFiles = 0
var deletedSize = 0
var failedFiles = 0
// 删除文件
for (fileURL, size) in filesToDelete {
do {
try FileManager.default.removeItem(at: fileURL)
} catch {
print("删除文件失败:\(fileURL.path),错误信息:\(error.localizedDescription)")
}
}
// 删除目录
for dirURL in dirsToDelete.reversed() {
do {
try FileManager.default.removeItem(at: dirURL)
} catch {
print("删除目录失败:\(dirURL.path),错误信息:\(error.localizedDescription)")
}
}
return true
}.value
}
enumerator 遍历并删除目录内容,也可以进行统计。
目录在这里需要使用 reversed 进行反向删除,原因在于:
temp/
├── folder1/
│ ├── file1.txt
│ └── subfolder/
│ └── file2.txt
└── folder2/
└── file3.txt
假设临时文件结构如上,enumerator 的遍历顺序为:
[
temp/folder1/, // 1. 目录
temp/folder1/file1.txt, // 2. 文件
temp/folder1/subfolder/, // 3. 子目录
temp/folder1/subfolder/file2.txt, // 4. 文件
temp/folder2/, // 5. 目录
temp/folder2/file3.txt // 6. 文件
]
如果正向删除,则无法统计目录中的文件和子目录。
1. 删除 folder1/ // 删除目录、文件和子目录
2. 删除 folder1/subfolder/ // 删除失败,已经在上一步被删除
3. 删除 folder2/ // 删除失败,已经在上一步被删除
反向删除则可以依次删除每一个子文件和子目录:
1. 删除 folder2/ // 删除成功
2. 删除 folder1/subfolder/ // 删除成功
3. 删除 folder1/ // 删除成功,folder1的内容已经为空
总结
清空目录内容操作在面临大量文件的目录时,可能会导致 UI 卡顿,建议使用后台线程处理删除操作。
