swift:nsurlsession下载文件

时间:2017-03-19 05:35:00

标签: ios swift nsurlsession

tableViewController个5个单元格。当我单击一个单元格时,如果找不到该文件,则开始下载。

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row > 0 {

        let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
        let documentDirectoryPath:String = path[0]
        let fileManager = FileManager()
        let destinationURLForFile = URL(fileURLWithPath: documentDirectoryPath.appendingFormat("/file%d.pdf",indexPath.row))

        if fileManager.fileExists(atPath: destinationURLForFile.path){ self)
        }else{

            var downloadTask: URLSessionDownloadTask!

            index = indexPath.row

            let url = URL(string: "http://publications.gbdirect.co.uk/c_book/thecbook.pdf")!
            downloadTask = backgroundSession.downloadTask(with: url)
            downloadTask.resume()

        }}

    func urlSession(_ session: URLSession,
                downloadTask: URLSessionDownloadTask,
                didFinishDownloadingTo location: URL){

    let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
    let documentDirectoryPath:String = path[0]
    let fileManager = FileManager()
    let destinationURLForFile = URL(fileURLWithPath: documentDirectoryPath.appendingFormat("/file%d.pdf",index))

    do {
        try fileManager.moveItem(at: location, to: destinationURLForFile)
        }catch{
            print("An error occurred while moving file to destination url")
        }
}

问题是,如果我一次下载2个文件,则下载其中一个文件会停止。如何解决?

2 个答案:

答案 0 :(得分:1)

有几点想法:

  1. 单个数字index属性显然不足以跟踪可能正在进行的多次下载。您需要一些结构来跟踪Documents文件夹中下载与其最终文件名之间的相关性。它可能是:

    struct Download {
        enum Status {
            case notStarted
            case started
            case finished
            case failed(Error?)
        }
    
        /// URL of resource on web server
    
        let remoteURL: URL
    
        /// URL of file in Documents folder
    
        let localURL: URL
    
        /// The status of the download
    
        var status: Status
    }
    

    现在你有一个类型来跟踪单个下载的状态,创建一个Download个对象的数组:

    var downloads = [Download]()
    
  2. 您可以在viewDidLoad中填充该内容,或类似内容:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        // create the `Download` objects, e.g. I'll create one here
    
        let remoteURL = URL(string: "http://publications.gbdirect.co.uk/c_book/thecbook.pdf")!
        let fileURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
            .appendingPathComponent("file0.pdf")
    
        let status: Download.Status
        if try! fileURL.checkResourceIsReachable() {
            status = .finished
        } else {
            status = .notStarted
        }
    
        downloads.append(Download(remoteURL: remoteURL, localURL: fileURL, status: status))
    
        // since you're dealing with background session (e.g. tasks may have been previously
        // scheduled), let's iterate through any pending tasks, updating status accordingly
    
        backgroundSession.getAllTasks { tasks in
            for task in tasks {
                guard let index = self.downloads.index(where: { $0.remoteURL == task.originalRequest?.url }) else {
                    print("cannot find download for \(task.originalRequest?.url)")
                    return
                }
                self.downloads[index].status = .started
            }
        }
    }
    
  3. 下载完成后,您现在可以在我们的下载数组中查找下载内容,以确定文件网址:

    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL){
        guard let index = downloads.index(where: { $0.remoteURL == downloadTask.originalRequest?.url }) else {
            print("cannot find download for \(downloadTask.originalRequest?.url)")
            return
        }
    
        do {
            try FileManager.default.moveItem(at: location, to: downloads[index].localURL)
            downloads[index].status = .finished
        } catch {
            print("An error occurred while moving file to destination url: \(error.localizedDescription)")
            downloads[index].status = .failed(error)
        }
    }
    
  4. 值得注意的是,如果文件不存在,则表示"然后开始下载"很可能是不够的。当然,如果文件存在,则完成下载。但是,如果已经开始下载,但还没有完成,该怎么办?如果之前发起的下载尚未完成,您可能不希望开始新的下载。

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if indexPath.row > 0 {
            let index = indexPath.row - 1   // for some reason you're looking at indexes greater than zero, so let's adjust our index for a zero-based index within our array
            switch downloads[index].status {
            case .notStarted:
                let downloadTask = backgroundSession.downloadTask(with: downloads[index].remoteURL)
                downloads[index].status = .started
                downloadTask.resume()
            default:
                break
            }
        }
    }
    
  5. 现在,我不想太过迷失上面这些代码片段的细节,而是我想确保你理解基本概念,即你不能拥有一个数字索引,但您需要一些集合(数组或字典)来跟踪在任何给定时间可能正在进行的所有各种下载。

答案 1 :(得分:0)

如果使用单个变量(index和downloadTask),则无法一次下载两个文件。每当用户选择第二个单元格时,就会设置一个新的索引值,因此在urlSession中使用该值:downloadTask:didFinishDownloadingTo:在第一个任务调用时是错误的。

您需要将这些值保留在集合中,例如元组数组,保留索引,任务以及有关该文件的任何其他信息,例如文件路径。