Networking with URLSession二 上传&下载

内容来自学习PART 6: DOWNLOAD AND UPLOAD TASK等系列课程,记录学习笔记

Networking with URLSession二 上传&下载

下载和上传Task

URLSessionDownloadTask

URLSessionDownloadTask直接把服务器的响应写入到一个临时文件中,并提供进度更新。如果在background session中使用download task,即使app程序被暂停或未运行,这些下载也会继续下载。

URLSession创建download task的方式

func downloadTask(with: URL,completionHandler: @escaping (URL?, URLResponse?, Error?) -> Void)
func downloadTask(with: URLRequest,completionHandler: @escaping (URL?, URLResponse?, Error?) -> Void)

downloadTask把数据保存在一个临时的文件中,所以在completion handler返回之前,必须读取和处理文件,或者把文件copy到一个永久的位置。否则文件会被删除,data会丢失。

func downloadTask(with: URL)
func downloadTask(with: URLRequest)

在调用过程中:

一个download task如果在完成之前被取消或者下载失败,可以保存resume data,并继续下载。

func cancel(byProducingResumeData:@escaping (Data?) -> Void)
func downloadTask(withResumeData: Data,completionHandler: @escaping (URL?,URLResponse?, Error?) -> Void)
func downloadTask(withResumeData: Data)

URLSessionDataTaskURLSessionUploadTask不同,NSURLSessionDownloadTask通过HTTP状态码,把服务器错误,报告给响应的NSError对象:

Server-side errorsNSErrors
401 UnauthorizedNSURLErrorUserAuthenticationRequired
403 ForbiddenNSURLErrorNoPermissionsToReadFile
407 Proxy Authentication RequiredNSURLErrorUserAuthenticationRequired
OtherNSURLErrorFileDoesNotExist

URLSessionUploadTask

URLSessionUploadTask与download task类似。可直接通过completionHandler来处理结果,而不通过代理

如下,上传data数据:

func uploadTask(with: URLRequest, from: Data?,completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void)
func uploadTask(with: URLRequest, from: Data)

但是不能直接使用URL来创建upload task,需要通过URLRequest来创建。因为upload task的http请求,需要http body。

除了上传data数据外,还可以上传file:

func uploadTask(with: URLRequest, fromFile: URL,completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void)
func uploadTask(with: URLRequest, fromFile: URL)

另外还有一个stream request

func uploadTask(withStreamedRequest: URLRequest)

各种优先级

1.URLSessionConfigurationnetworkServiceType属性对标准的网络流量,网络电话,语音,视频,以及由一个后台进程使用的流量进行了区分。值可为default, voip, video, background, voice
2.URLSessionTaskpriority属性。即你希望host处理任务的相对优先级。0.0 (lowest)1.0 (highest), default是0.5
3.URLRequestnetworkServiceType。可重写配置对象的networkServiceType
4.URLSession’s delegateQueuequalityOfService: userInteractive, userInitiated, utility, background

缓存

大量的下载可以很好的利用缓存来减少网络拥堵的问题。Default configuration使用的是一个持久的基于disc的cache。
cache会减少app对网络连接的依赖,提高的app的性能。
默认的cache policy使用的是protocol的cache policy,而protocol通常是HTTP。

缓存流程

参考文档:


下载

本例下载,是下载音频示例,如:

http://audio.itunes.apple.com/apple-assets-us-std-000001/AudioPreview30/v4/02/4e/35/024e3534-92b4-6e6d-217e-b5714b6faa20/mzaf_5572571419358472776.plus.aac.p.m4a

开始下载资源
代码如下:

  func startDownload(_ track: Track) {
    let download = Download(url: track.previewURL)
    //下载任务
    download.task = defaultSession.downloadTask(with: track.previewURL) { location, response, error in
      self.saveDownload(download: download, location: location, response: response, error: error)
    }
    download.task!.resume()
    download.isDownloading = true
    activeDownloads[download.url] = download
  }

  func saveDownload(download : Download, location : URL?, response : URLResponse?, error : Error?) {
    let sourceURL = download.url
    if error != nil { return }

    activeDownloads[sourceURL] = nil
    //copy目的地地址
    let destinationURL = localFilePath(for: sourceURL)
    //先移除在复制
    let fileManager = FileManager.default
    try? fileManager.removeItem(at: destinationURL)
    do {
      try fileManager.copyItem(at: location!, to: destinationURL)
    } catch let error {
      print("Could not copy file to disk: \(error.localizedDescription)")
    }
    //更新UI
    if let index = trackIndex(for: download.task!) {
      DispatchQueue.main.async {
        self.tableView.reloadRows(at: [IndexPath(row: index, section: 0)], with: .none)
      }
    }
  }

暂停下载

  //暂停下载
  func pauseDownload(_ track: Track) {
    guard let download = activeDownloads[track.previewURL] else { return }
    //处理已下载的数据
    download.task!.cancel(byProducingResumeData: { (data) in
      download.resumeData = data
    })

    download.isDownloading = false
    //更新UI
    if let index = trackIndex(for: download.task!) {
      DispatchQueue.main.async {
        self.tableView.reloadRows(at: [IndexPath(row: index, section: 0)], with: .none)
      }
    }
  }

取消下载

  //取消下载
  func cancelDownload(_ track: Track) {

    guard let download = activeDownloads[track.previewURL] else { return }
    //取消下载
    download.task!.cancel()
    download.isDownloading = false
    activeDownloads[track.previewURL] = nil

    if let index = trackIndex(for: download.task!) {
      DispatchQueue.main.async {
        self.tableView.reloadRows(at: [IndexPath(row: index, section: 0)], with: .none)
      }
    }
  }

继续下载

  // 继续下载
  func resumeDownload(_ track: Track) {

    guard let download = activeDownloads[track.previewURL] else { return }
    if let resumeData = download.resumeData {
      //在原来的数据上继续下载
      download.task = defaultSession.downloadTask(withResumeData: resumeData, completionHandler: { (location, response, error) in
        self.saveDownload(download: download, location: location, response: response, error: error)
      })

    }else {
      //重新下载
      download.task = defaultSession.downloadTask(with: download.url, completionHandler: { (location, response, error) in
        self.saveDownload(download: download, location: location, response: response, error: error)
      })
    }

    download.task!.resume()
    download.isDownloading = true

    if let index = trackIndex(for: download.task!) {
      DispatchQueue.main.async {
        self.tableView.reloadRows(at: [IndexPath(row: index, section: 0)], with: .none)
      }
    }

  }

上传

上传以上一节的PostRouter为例,上传json数据

let session = URLSession(configuration: .ephemeral)
let putRequest = PostRouter.update(1, ["author": "Part 6", "title": "Upload Task"]).asURLRequest()
typealias JSONDictionary = [String: Any]
let putTask = session.uploadTask(with: putRequest, from: putRequest.httpBody) { data, response, error in
  defer { PlaygroundPage.current.finishExecution() }
  guard let data = data, let response = response as? HTTPURLResponse,
    response.statusCode == 200 else {
      print("No data or statusCode not OK")
      return
  }

  var jsonResult: JSONDictionary
  do {
    let result = try JSONSerialization.jsonObject(with: data, options: [])
    jsonResult = result as! JSONDictionary
  } catch {
    print(error.localizedDescription)
    return
  }
}
putTask.resume()

其它文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值