FilesProvider 0.26.0

FilesProvider 0.26.0

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布最后发布2019年4月
SPM支持 SPM

Amir Abbas Mousavian维护。



File Provider

这个 Swift 库提供了一种统一处理本地和远程文件及目录的方式。

Swift Version Platform License Build Status Codebeat Badge

Release version CocoaPods version Carthage compatible Cocoapods Downloads Cocoapods Apps

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

所有功能都进行异步调用,且不会阻塞主线程。

功能

  • LocalFileProvider 是围绕 FileManager 进行的包装,增加了内置协调、搜索和读取文件片段的功能。
  • CloudFileProvider 模拟应用中的 iCloud Drive 的通用容器 API。
  • WebDAVFileProvider WebDAV 协议是事实上的文件传输标准,某些云服务(如 ownCloudBox.comYandex.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/OAuth2OAuthSwift可以简化检索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)

将文件复制到和从本地存储

在提供者和本地存储之间下载和上传文件有两种方法。这些方法使用 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 提供程序允许开发人员使用 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

https://github.com/amosavian/