Swift - 网络抽象层库Moya的使用详解6(文件下载、资源下载器)
作者:hangge | 2017-10-16 08:10
九、文件下载的基本用法
1,网络层定义(MyServiceAPI.swift)
这里我只定义了一个枚举值 downloadAsset 用来表示资源下载请求,具体功能如下:
- 通过传入的资源名称(assetName)自动从 http://www.hangge.com/assets/ 路径下面下载对应的文件。
- 下载后文件保存在用户文档目录中,且不修改文件名(原来叫什么还叫什么)。
- 如果目录中已经存在有同名文件,新下载的文件不会覆盖老的。
import Moya
//初始化请求的provider
let MyServiceProvider = MoyaProvider<MyService>()
//请求分类
public enum MyService {
case downloadAsset(assetName:String) //下载文件
}
//请求配置
extension MyService: TargetType {
//服务器地址
public var baseURL: URL {
return URL(string: "http://www.hangge.com")!
}
//各个请求的具体路径
public var path: String {
switch self {
case let .downloadAsset(assetName):
return "/assets/\(assetName)"
}
}
//请求类型
public var method: Moya.Method {
return .get
}
//请求任务事件(这里附带上参数)
public var task: Task {
switch self {
case .downloadAsset(_):
return .downloadDestination(DefaultDownloadDestination)
}
}
//是否执行Alamofire验证
public var validate: Bool {
return false
}
//这个就是做单元测试模拟的数据,只会在单元测试文件中有作用
public var sampleData: Data {
return "{}".data(using: String.Encoding.utf8)!
}
//请求头
public var headers: [String: String]? {
return nil
}
}
//定义下载的DownloadDestination(不改变文件名,同名文件不会覆盖)
private let DefaultDownloadDestination: DownloadDestination = { temporaryURL, response in
return (DefaultDownloadDir.appendingPathComponent(response.suggestedFilename!), [])
}
//默认下载保存地址(用户文档目录)
let DefaultDownloadDir: URL = {
let directoryURLs = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask)
return directoryURLs.first ?? URL(fileURLWithPath: NSTemporaryDirectory())
}()
2,使用样例
比如我们这里传入一个"logo.png",Moya 便自动将 http://www.hangge.com/assets/logo.png 这张图片下载下来。
//要下载的图片名称
let assetName = "logo.png"
//通过Moya进行下载
MyServiceProvider.request(.downloadAsset(assetName: assetName)) { result in
switch result {
case .success:
let localLocation: URL = DefaultDownloadDir.appendingPathComponent(assetName)
let image = UIImage(contentsOfFile: localLocation.path)
print("下载完毕!保存地址:\(localLocation)")
case let .failure(error):
print(error)
}
}
3,运行结果
在上面使用样例代码中,我们在资源下载完毕后会将文件最终的保存位置打印出来,控制台输出信息如下:

十、文件下载的进阶用法
1,获取下载进度
(1)通过 progress 回调函数,我们可以实时得到当前的下传进度。
//要下载的图片名称
let assetName = "logo.png"
//通过Moya进行下载
MyServiceProvider.request(.downloadAsset(assetName: assetName), progress:{
progress in
//实时打印出下载进度
print("当前进度: \(progress.progress)")
}) { result in
switch result {
case .success:
let localLocation: URL = DefaultDownloadDir.appendingPathComponent(assetName)
let image = UIImage(contentsOfFile: localLocation.path)
print("下载完毕!保存地址:\(localLocation)")
case let .failure(error):
print(error)
}
}
(2)可以看到控制台不断输出已下载的进度(1 则表示下载完毕):

2,自动覆盖同名文件
如果想要下载时遇到同名的文件也能覆盖写入,在创建 DownloadDestination 对象时增加一个 .removePreviousFile 配置即可。
//定义下载的DownloadDestination(不改变文件名,遇到同名文件会覆盖)
private let DefaultDownloadDestination: DownloadDestination = { temporaryURL, response in
return (DefaultDownloadDir.appendingPathComponent(response.suggestedFilename!),
[.removePreviousFile])
}
3,自定义文件保存的名字
(1)如果想要下载下来的文件改名,可以对 MyServiceAPI.swift 进行修改,在下载请求中增加一个参数(saveName),表示文件保存的名字。
import Moya
//初始化请求的provider
let MyServiceProvider = MoyaProvider<MyService>()
//请求分类
public enum MyService {
case downloadAsset(assetName:String, saveName:String) //下载文件
}
//请求配置
extension MyService: TargetType {
//服务器地址
public var baseURL: URL {
return URL(string: "http://www.hangge.com")!
}
//各个请求的具体路径
public var path: String {
switch self {
case let .downloadAsset(assetName, _):
return "/blog/images/\(assetName)"
}
}
//请求类型
public var method: Moya.Method {
return .get
}
//请求任务事件(这里附带上参数)
public var task: Task {
switch self {
case let .downloadAsset(_, saveName):
let localLocation: URL = DefaultDownloadDir.appendingPathComponent(saveName)
let downloadDestination:DownloadDestination = { _, _ in
return (localLocation, .removePreviousFile) }
return .downloadDestination(downloadDestination)
}
}
//是否执行Alamofire验证
public var validate: Bool {
return false
}
//这个就是做单元测试模拟的数据,只会在单元测试文件中有作用
public var sampleData: Data {
return "{}".data(using: String.Encoding.utf8)!
}
//请求头
public var headers: [String: String]? {
return nil
}
}
//默认下载保存地址(用户文档目录)
let DefaultDownloadDir: URL = {
let directoryURLs = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask)
return directoryURLs.first ?? URL(fileURLWithPath: NSTemporaryDirectory())
}()
(2)下面是一个使用样例,我们将下载下来的文件自动以当前时间的时间戳命名。
//要下载的图片名称
let assetName = "logo.png"
//保存的名称
let timeInterval:TimeInterval = Date().timeIntervalSince1970
let saveName = "\(Int(timeInterval)).png"
//通过Moya进行下载
MyServiceProvider.request(.downloadAsset(assetName: assetName, saveName: saveName)) {
result in
switch result {
case .success:
let localLocation: URL = DefaultDownloadDir.appendingPathComponent(saveName)
let image = UIImage(contentsOfFile: localLocation.path)
print("下载完毕!保存地址:\(localLocation)")
case let .failure(error):
print(error)
}
}
(3)保存结果如下:

十一、封装一个资源下载器
有时我们会将一些资源文件(比如图片、声音、皮肤等)放置在服务器上,而不是直接打包到应用程序中,当程序要使用时资源时再从服务器上加载。这样的话以后如果需要修改图片等资源文件,只需把服务器上的文件替换即可,客户端这边就不需要再更新程序了。
客户端这边我们可以像上面那样直接通过 Moya 去请求资源(传入一个文件名,然后自动将其下载下来)。但这样做还是有些不方便的地方,可能我们的资源文件很多,要使用的地方也很多,每个地方都手动拼写文件名容易出错,也不容易记住。我们可以封装一个资源下载器方便使用。
1,资源下载器(AssetLoader.swift)
- 其本质还是通过 Moya 进行文件的下载,只不过这里我们将每一个资源都定义成一个枚举值。这样下载某个资源时只要调用对应的枚举值即可,方便使用。
- 同时在 MoyaProvider 外面再封装一层(AssetLoader),它会处理请求结果并返回文件存放路径。而且每次请求资源时会先判断本地是否已经存在该文件,如果已经下载过了就直接返回路径。
import Moya
import Alamofire
//默认下载保存地址(用户文档目录)
fileprivate let assetDir: URL = {
let directoryURLs = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask)
return directoryURLs.first ?? URL(fileURLWithPath: NSTemporaryDirectory())
}()
//资源分类
public enum Asset {
case logo //logo图标
case star //星形图标
case checkmark //勾选图标
}
//资源配置
extension Asset: TargetType {
//根据枚举值获取对应的资源文件名
var assetName: String {
switch self {
case .logo: return "logo.png"
case .star: return "star.png"
case .checkmark: return "checkmark.png"
}
}
//获取对应的资源文件本地存放路径
var localLocation: URL {
return assetDir.appendingPathComponent(assetName)
}
//服务器地址
public var baseURL: URL {
return URL(string: "http://www.hangge.com")!
}
//各个请求的具体路径
public var path: String {
return "/assets/" + assetName
}
//请求类型
public var method: Moya.Method {
return .get
}
//定义一个DownloadDestination
var downloadDestination: DownloadDestination {
return { _, _ in return (self.localLocation, .removePreviousFile) }
}
//请求任务事件
public var task: Task {
return .downloadDestination(downloadDestination)
}
//是否执行Alamofire验证
public var validate: Bool {
return false
}
//这个就是做单元测试模拟的数据,只会在单元测试文件中有作用
public var sampleData: Data {
return "{}".data(using: String.Encoding.utf8)!
}
//请求头
public var headers: [String: String]? {
return nil
}
}
//资源下载器
final class AssetLoader {
let provider = MoyaProvider<Asset>()
init() { }
func load(asset: Asset, completion: ((Result<Any>) -> Void)? = nil) {
if FileManager.default.fileExists(atPath: asset.localLocation.path) {
completion?(.success(asset.localLocation))
return
}
provider.request(asset) { result in
switch result {
case .success:
completion?(.success(asset.localLocation))
case let .failure(error):
completion?(.failure(error))
}
}
}
}
2,使用样例
比如这里我们使用 AssetLoader 加载一个 logo 图标。
let loader = AssetLoader()
loader.load(asset: .logo) { result in
switch result {
case let .success(localLocation):
print("下载完毕!保存地址:\(localLocation)")
case let .failure(error):
print(error)
}
}
全部评论(1)
请问站站,Moya如何支持断点下载呢?
站长回复:我也不知道办法,Moya好像就不支持断点下载,暂时帮不了你了。