这个 Swift 库提供了一种统一处理本地和远程文件及目录的方式。
该库实现了 WebDav、FTP、Dropbox、OneDrive 和 SMB2(不完整)以及本地文件。
所有功能都进行异步调用,且不会阻塞主线程。
功能
- LocalFileProvider 是围绕
FileManager
进行的包装,增加了内置协调、搜索和读取文件片段的功能。 - CloudFileProvider 模拟应用中的 iCloud Drive 的通用容器 API。
- WebDAVFileProvider WebDAV 协议是事实上的文件传输标准,某些云服务(如
ownCloud
、Box.com
和Yandex.disk
)支持该标准。 - FTPFileProvider 虽然 1990 年代就已经被淘汰,但由于安全问题,它仍然在一些网络主机上使用。
- DropboxFileProvider 模拟 Dropbox Web API。
- 目前它在上传文件到 150MB 时有限制。
- OneDriveFileProvider 模拟 OneDrive REST API,适合同样名称的网站和兼容(商务)服务器。
- AmazonS3FileProvider Amazon 存储后端。许多网站的所用。
- GoogleFileProvider 模拟 Google Drive REST API。
- SMBFileProvider SMB2/3 是 2006 年引入的,它起源于 Microsoft Windows 的文件和打印机共享协议,现在正在取代 macOS 上的 AFP 协议。
- 已经实现了数据类型和一些基本功能,但 主界面尚未实现!
- 目前,您可以使用 amosavian/AMSMB2 框架连接到 SMB2。
要求
- Swift 4.0 或更高版本
- iOS 8.0 ,OSX 10.10
- XCode 9.0
旧版本可在swift-3分支中找到。
将此行添加到您的pods文件中
pod "FilesProvider"
或添加到Cartfile中
github "amosavian/FileProvider"
或要在Swift Package Manager中使用,请在Dependencies
中添加此行
.Package(url: "https://github.com/amosavian/FileProvider.git", majorVersion: 0)
要轻松获取最新更新,请在终端中使用以下命令进行克隆
git clone https://github.com/amosavian/FileProvider
您可以在FileProvider文件夹中使用此命令更新您的库
git pull
如果您有一个基于git的项目,请使用以下命令在项目目录中添加此项作为子模块到您项目中
git submodule add https://github.com/amosavian/FileProvider
然后您可以执行以下任意一项
-
将源文件夹复制到您的项目中,Voila!
-
将
FilesProvider.xcodeproj
拖放到您的Xcode工作区中,并将框架添加到目标的嵌入二进制文件中。
要查找设计概念和如何实现自定义提供者,请阅读概念和设计文档。
使用方法
每个提供者都有一个特定的类,它遵循FileProvider协议,并且使用相同的语法。
在这里可以找到iOS的示例代码。
初始化
对于LocalFileProvider,如果您要处理Documents
文件夹
import FilesProvider
let documentsProvider = LocalFileProvider()
// Equals with:
let documentsProvider = LocalFileProvider(for: .documentDirectory, in: .userDomainMask)
// Equals with:
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let documentsProvider = LocalFileProvider(baseURL: documentsURL)
同样,对于使用共享容器
import FilesProvider
let documentsProvider = LocalFileProvider(sharedContainerId: "group.yourcompany.appContainer")
// Replace your group identifier
之后不能更改基本URL,默认情况下所有路径都与该基本URL相关联。
要初始化iCloud容器提供商,请查阅这里了解如何更新项目设置,然后使用以下代码,这将自动管理在容器中创建Documents文件夹
import FilesProvider
let documentsProvider = CloudFileProvider(containerId: nil)
对于远程文件提供商,可能需要进行认证
import FilesProvider
let credential = URLCredential(user: "user", password: "pass", persistence: .permanent)
let webdavProvider = WebDAVFileProvider(baseURL: URL(string: "http://www.example.com/dav")!, credential: credential)
-
如果您想在iOS 9+ / macOS 10.11+上连接非安全服务器(WebDAV(http)或FTP),您应根据本指南禁用App Transport Security(ATS)。
-
对于Dropbox和OneDrive,用户是clientID,密码是Token,都必须通过OAuth2 API检索。有一些库,如p2/OAuth2或OAuthSwift可以简化检索Token的过程。后者更容易使用,更受欢迎。请参阅用于Dropbox和OneDrive的OAuth示例以获取详细说明。
对于与UI的交互,设置FileProvider
对象的delegate属性。
如果提供商允许,您可以使用url(of:)
方法获取直接访问URL(本地或远程文件)。
代理
为了更新用户界面,请考虑使用代理方法而不是完成处理程序。代理方法保证在主线程中运行,以避免错误。
有三种方法:指示操作是否失败、成功以及操作完成了多少(适用于上传和下载操作)。
您的类应该符合FileProviderDelegate
类
override func viewDidLoad() {
documentsProvider.delegate = self as FileProviderDelegate
}
func fileproviderSucceed(_ fileProvider: FileProviderOperations, operation: FileOperationType) {
switch operation {
case .copy(source: let source, destination: let dest):
print("\(source) copied to \(dest).")
case .remove(path: let path):
print("\(path) has been deleted.")
default:
print("\(operation.actionDescription) from \(operation.source!) to \(operation.destination) succeed")
}
}
func fileproviderFailed(_ fileProvider: FileProviderOperations, operation: FileOperationType) {
switch operation {
case .copy(source: let source, destination: let dest):
print("copy of \(source) failed.")
case .remove:
print("file can't be deleted.")
default:
print("\(operation.actionDescription) from \(operation.source!) to \(operation.destination) failed")
}
}
func fileproviderProgress(_ fileProvider: FileProviderOperations, operation: FileOperationType, progress: Float) {
switch operation {
case .copy(source: let source, destination: let dest):
print("Copy\(source) to \(dest): \(progress * 100) completed.")
default:
break
}
}
注意:目前fileproviderProgress()
代理方法不会被LocalFileProvider
调用。
建议使用完成处理程序进行错误处理或结果处理。
控制文件操作
您还可以实现 FileOperationDelegate
协议来控制文件操作行为(复制、移动/重命名、删除和链接),并决定哪些文件应该被删除等。
fileProvider(shouldDoOperation:)
方法在执行操作之前被调用。如果您想执行操作,应该返回 true
;如果您想停止该操作,应该返回 false
。
fileProvider(shouldProceedAfterError:, operation:)
将在文件操作过程中出错时被调用。如果想在下一个文件上继续操作,则返回 true
;如果想在进一步操作,则返回 false
。如果没有实现代理,则默认值为 false
。
注意:在 LocalFileProvider
中,这些方法将递归地针对目录及其子目录中的文件调用。
目录内容和文件属性
存在一个 FileObject
类,它包含文件属性,如大小和创建日期。您可以检索目录内文件的信息,或直接获取文件信息。
对于一个单个文件:
documentsProvider.attributesOfItem(path: "/file.txt", completionHandler: {
attributes, error in
if let attributes = attributes {
print("File Size: \(attributes.size)")
print("Creation Date: \(attributes.creationDate)")
print("Modification Date: \(attributes.modifiedDate)")
print("Is Read Only: \(attributes.isReadOnly)")
}
})
要获取目录中文件的列表:
documentsProvider.contentsOfDirectory(path: "/", completionHandler: {
contents, error in
for file in contents {
print("Name: \(file.name)")
print("Size: \(file.size)")
print("Creation Date: \(file.creationDate)")
print("Modification Date: \(file.modifiedDate)")
}
})
要获取存储空间和已用/可用空间的大小:
func storageProperties(completionHandler: { total, used in
print("Total Storage Space: \(total)")
print("Used Space: \(used)")
print("Free Space: \(total - used)")
})
- 如果此功能在提供者上不可用或发生错误,则总空间报告为
-1
,已用空间为0
。
创建文件和文件夹
创建新目录
documentsProvider.create(folder: "new folder", at: "/", completionHandler: { error in
if let error = error {
// Error handling here
} else {
// The operation succeed
}
})
要创建文件,使用 writeContents(path:, content:, atomically:, completionHandler:)
方法。
复制和移动/重命名文件
将当前路径下的文件 old.txt 复制到 new.txt
documentsProvider.copyItem(path: "new folder/old.txt", to: "new.txt", overwrite: false, completionHandler: nil)
将当前路径下的文件 old.txt 移动到 new.txt
documentsProvider.moveItem(path: "new folder/old.txt", to: "new.txt", overwrite: false, completionHandler: nil)
注意:为确保行为一致,如果需要,请先创建中间目录。
删除文件
documentsProvider.removeItem(path: "new.txt", completionHandler: nil)
获取文件内容
此目的有两种方法,其中之一将整个文件加载到 Data
中,另一个可以加载文件的一部分。
documentsProvider.contents(path: "old.txt", completionHandler: {
contents, error in
if let contents = contents {
print(String(data: contents, encoding: .utf8)) // "hello world!"
}
})
如果需要检索文件的一部分,可以使用带偏移量和长度参数的 contents
方法。请注意,文件的第一字节偏移量为:0。
documentsProvider.contents(path: "old.txt", offset: 2, length: 5, completionHandler: {
contents, error in
if let contents = contents {
print(String(data: contents, encoding: .utf8)) // "llo w"
}
})
将数据写入文件
let data = "What's up Newyork!".data(encoding: .utf8)
documentsProvider.writeContents(path: "old.txt", content: data, atomically: true, completionHandler: nil)
将文件复制到和从本地存储
在提供者和本地存储之间下载和上传文件有两种方法。这些方法使用 URLSessionDownloadTask
和 URLSessionUploadTask
类,并允许使用后台会话并通过代理提供进度。
要上传文件
let fileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("image.jpg")
documentsProvider.copyItem(localFile: fileURL, to: "/upload/image.jpg", overwrite: true, completionHandler: nil)
要下载文件
let fileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("image.jpg")
documentsProvider.copyItem(path: "/download/image.jpg", toLocalURL: fileURL, overwrite: true, completionHandler: nil)
- 只能假设这些方法 不会 以递归方式处理目录的上传/下载。如果需要,可以使用这些方法列出目录、在目标上创建目录并复制文件。
- FTP 提供程序允许开发人员使用 apple 实现的
URLSessionDownloadTask
或基于流任务的自定义实现方法(通过useAppleImplementation
属性)。FTP 协议不支持后台会话。
操作进度
创建/复制/删除/搜索功能返回一个(NS)Progress
。它提供操作类型、进度以及一个.cancel()
方法,允许你在操作进行过程中取消操作。你可以检查cancellable
属性来查看是否可以通过此对象取消操作。
- 注意:本地
(NS)FileManager
不支持进度报告,所以使用了LocalFileProvider
。
撤销操作
遵守FileProviderUndoable
的提供者可以执行某些操作,如移动/重命名、复制和创建(文件或文件夹)。目前,只有LocalFileProvider
支持此功能。要实现
// To setup a new UndoManager:
documentsProvider.setupUndoManager()
// or if you have an UndoManager object already:
documentsProvider.undoManager = self.undoManager
// e.g.: To undo last operation manually:
documentsProvider.undoManager?.undo()
您还可以将UndoManager
对象与视图控制器绑定以使用摇晃手势和iOS/macOS内置的撤销支持,像以下示例代码一样将代码添加到您的ViewController类中:
class ViewController: UIViewController
override var canBecomeFirstResponder: Bool {
return true
}
override var undoManager: UndoManager? {
return (provider as? FileProvideUndoable)?.undoManager
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Your code here
UIApplication.shared.applicationSupportsShakeToEdit = true
self.becomeFirstResponder()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Your code here
UIApplication.shared.applicationSupportsShakeToEdit = false
self.resignFirstResponder()
}
// The rest of your implementation
}
文件协调
LocalFileProvider
及其子类有一个isCoordinating
属性。通过设置此属性,提供者将使用NSFileCoordinating
类进行所有文件操作。这在使用iCloud时是强制性的,在共享容器或文件/文件夹同时操作常见的任何地方都是推荐的。
监控文件更改
您可以在某些文件系统(本地和SMB2)中监控更新,支持提供者有三个方法可供您使用:注册处理器、注销处理器和检查是否正在被监控。当目录中增加或删除新文件时,这对于更新用户界面非常有用。处理器将在主线程中分发,以避免与UI相关的0.25秒延迟的虫虫效应。
// to register a new notification handler
documentsProvider.registerNotifcation(path: "/") {
// calling functions to update UI
}
// To discontinue monitoring folders:
documentsProvider.unregisterNotifcation(path: "/")
- 请注意在LocalFileProvider中,它也会监控子文件夹中的更改。这种行为根据文件系统规范可能不同。
缩略图和元信息
符合ExtendedFileProvider
协议的提供者可以生成缩略图或提供图片、媒体和PDF文件的元信息。
本地、OneDrive和Dropbox提供者支持此功能。
缩略图
要检查是否支持文件缩略图并获取缩略图,请使用(并修改)以下示例代码
let path = "/newImage.jpg"
let thumbSize = CGSize(width: 64, height: 64) // or nil which renders to default dimension of provider
if documentsProvider.thumbnailOfFileSupported(path: path {
documentsProvider.thumbnailOfFile(path: file.path, dimension: thumbSize, completionHandler: { (image, error) in
// Interacting with UI must be placed in main thread
DispatchQueue.main.async {
self.previewImage.image = image
}
}
}
- 请注意,它不会缓存生成的图像。如果您不自己处理,可能会影响您应用程序的性能。
元信息
要获取图像/视频的拍摄日期、位置、尺寸等元信息,请使用(并修改)以下示例代码
if documentsProvider..propertiesOfFile(path: file.path, completionHandler: { (propertiesDictionary, keys, error) in
for key in keys {
print("\(key): \(propertiesDictionary[key])")
}
}
- 附加内容:您可以通过设置
LocalFileInformationGenerator
的静态变量和方法来修改/扩展本地提供者生成器
贡献
我们非常希望您为FileProvider做出贡献,有关更多信息,请检查LICENSE
文件。
您可以考虑帮助我们的事项
- 为
SMBClient
实现请求/响应堆栈 - 实现测试用例(
XCTest
) - 添加iOS示例项目
- 添加macOS示例项目
使用中的项目
如果您已在本项目中使用此库,您可以提出问题以通知我们。
元信息
Amir-Abbas Mousavian – @amosavian
感谢Hootan Moradi设计Logo。
在MIT许可下分发。有关更多信息,请参阅LICENSE
。