FileProvider 0.17.0

FileProvider 0.17.0

测试已测试
Lang语言 SwiftSwift
许可证 MIT
发布最新发布2017年7月
SwiftSwift 版本3.0
SPM支持 SPM

Amir Abbas Mousavian 维护。



  • Amir Abbas Mousavian

File Provider

该 Swift 库提供了一种统一处理本地和远程文件和目录的方法。

Codebeat Badge

该库实现了 WebDAV、FTP、Dropbox、OneDrive 和 SMB2(不完整)以及本地文件。

所有功能都执行异步调用,不会阻塞您的主线程。

特性

  • [x] LocalFileProviderFileManager 的封装,增加了一些功能,如内置协调、搜索和读取文件的一部分。
  • [x] CloudFileProvider 是 App 的通用容器 API 的封装,即 iCloud Drive。
  • [x] WebDAVFileProvider WebDAV 协议是事实上的文件传输标准,由一些云服务支持,如 ownCloudBox.comYandex.disk
  • [x] FTPFileProvider 虽然 1990 年代因严重的安全问题被弃用,但是一些 Web 托管商仍在使用。
    • 尚未实现被动模式。

  • [x] DropboxFileProvider 是 Dropbox Web API 的封装。
    • 现在上传文件的大小有限制,最多 150MB。

  • [x] OneDriveFileProvider 是 OneDrive REST API 的封装,适用于 onedrive.com 以及兼容(商业)服务器。
    • 现在上传文件的大小有限制,最多 100MB。

  • [ ] GoogleFileProvider 是 Google Drive REST API 的封装。
  • [ ] AmazonS3FileProvider 是亚马逊存储后端。被许多网站使用。
  • [ ] SMBFileProvider SMB2/3 是 2006 年引入的,最初是来自 Microsoft Windows 的文件和打印机共享协议,现在正在取代 macOS 上的 AFP 协议。
    • 已实现数据类型和一些基本函数,但 主要界面尚未实现!
    • SMB1/CIFS 已弃用,并在 Swift 中极为复杂,因为对内存对齐有严格要求。

要求

  • Swift 3.0 或更高版本
  • iOS 8.0,OSX 10.10
  • XCode 8.0

可以在 swift-2 分支中找到旧的版本。

安装

手动

为了轻松获取最新更新,请在终端中使用此命令以克隆:

git clone https://github.com/amosavian/FileProvider

您可以使用此命令在 FileProvider 文件夹中更新您的库:

git pull

如果您有一个基于 Git 的项目,请在项目目录中使用此命令将此项目作为 submod

git submodule add https://github.com/amosavian/FileProvider

然后您可以执行以下任一操作:

  • 将源文件夹复制到您的项目中,万事大吉!

  • FileProvider.xcodeproj拖放到您的 Xcode 工作区中,并将其框架添加到目标的嵌入式二进制文件中。

用法

每个提供者都有一个特定的类,该类符合 FileProvider 协议,并具有相同的语法

初始化

针对 LocalFileProvider,如果您想处理Documents文件夹

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)

也适用于使用共享容器

let documentsProvider = LocalFileProvider(sharedContainerId: "group.yourcompany.appContainer")
// Replace your group identifier

之后您无法更改基本 URL。默认情况下,所有路径都与这个基本 URL 相关。

要初始化 iCloud Container 提供者,请参阅这里了解如何更新项目设置,然后使用以下代码,这将自动管理在容器中创建 Documents 文件夹

let documentsProvider = CloudFileProvider(containerId: nil)

对于远程文件提供者,可能需要进行身份验证

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) 服务器,您应该根据此指南禁用 App Transport Security (ATS)。

  • 对于 Dropbox 和 OneDrive,用户是 clientID,密码是 Token,这两者都必须通过Dropbox 的 OAuth2 API获取。有一些库,例如p2/OAuth2OAuthSwift,可以简化获取 Token 的过程。后者使用起来更简单,也更为推荐。

要交互 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: \(attributes.name)")
        print("Size: \(attributes.size)")
        print("Creation Date: \(attributes.creationDate)")
        print("Modification Date: \(attributes.modifiedDate)")
    }
})

要获取存储空间的大小和已用/可用空间

func storageProperties(completionHandler: { total, used in
    print("Total Storage Space: \(total)")
    print("Used Space: \(used)")
    print("Free Space: \(total - used)")
})
  • 如果该功能在提供方不可用或发生了错误,将报告总空间为-1,已用空间为0

更改当前目录

documentsProvider.currentPath = "/New Folder"
// now path is ~/Documents/New Folder

然后,您可以传递空字符串("")到contentsOfDirectory方法以列出当前目录中的文件。

创建文件和文件夹

创建新目录

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)

警告:该方法会递归删除包含所有内容的目录(除了不支持SITE RMDIR命令的FTP提供方,这将稍后修复)。

获取文件内容

有两种方法可以实现这一目的,其中一种将整个文件加载到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)

从/到本地URL的文件复制

有两种方法可以在提供方和本地存储之间下载和上传文件。这些方法使用URLSessionDownloadTaskURLSessionUploadTask类,允许使用后台会话并通过代理提供进度。

要上传文件

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提供方允许开发人员使用苹果实现的URLSessionDownloadTask或者基于流任务的定制实现方法,这可以通过useAppleImplementation属性完成。FTP协议不支持后台会话。

撤销操作

遵守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
}

操作句柄

创建/复制/删除函数返回一个对远程操作的OperationHandle。它提供操作类型、进度和允许您在中途取消操作的.cancel()方法。

原生(NS)FileManager不支持此功能,因此LocalFileProvider不支持,但此功能将添加到未来的PosixFileProvider类。

文件协调

LocalFileProvider及其子类都有一个isCoordinating属性。通过设置这个属性,提供者将使用NSFileCoordinating类来进行所有的文件操作。对于iCloud是强制性的,当使用共享容器或在通常对文件/文件夹进行并行操作的任何地方,则强烈推荐使用。

监控文件更改

您可以在某些文件系统(本地和SMB2)中监控更新,在支持提供者的三个方法中,您可以用来注册处理程序、注销注册以及检查是否正在监控。当需要在目录中添加或删除新文件以及更新用户界面时,这个功能很有用。处理程序将被调度到主线程,以避免与UI相关的错误,延迟为0.25秒。

// to register a new notification handler
documentsProvider.registerNotifcation(path: provider.currentPath) {
    // calling functions to update UI 
}
    
// To discontinue monitoring folders:
documentsProvider.unregisterNotifcation(path: provider.currentPath)
  • 请注意在LocalFileProvider中,它还会监控子文件夹中的更改。这种行为可能会根据文件系统规范而有所不同。

缩略图和元信息

符合ExtendedFileProvider规范的提供者能够为图像、媒体和PDF文件生成缩略图或提供文件元信息。

Local、OneDrive和Dropbox提供者支持此功能。

缩略图

要检查文件是否支持缩略图并获取缩略图,请使用(并修改)以下示例代码

let path = "/newImage.jpg"
let thumbSize = CGSize(width: 64, height: 64)
if documentsProvider.thumbnailOfFileSupported(path: path {
    documentsProvider.thumbnailOfFile(path: file.path, dimension: thumbSize, completionHandler: { (image, error) in
        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的静态变量和方法来修改/扩展Local提供者生成器

贡献力量

我们非常欢迎您为FileProvider贡献力量,更多详情请查看LICENSE文件。

您可以考虑去做的一些事情来帮助我们

  • [ ] 实现SMBClient的请求/响应堆栈
  • [ ] 实现测试用例(《XCTest》)
  • [ ] 为iOS添加示例项目
  • [ ] 为macOS添加示例项目

正在使用的项目

如果您在使用此库的项目中,您可以打开一个issue来通知我们。

元信息

Amir-Abbas Mousavian – @amosavian

感谢Hootan Moradi设计标志。

在MIT许可证下分发。更多信息请参阅LICENSE文件。

https://github.com/amosavian/