【问题标题】:Download file from server using Swift使用 Swift 从服务器下载文件
【发布时间】:2015-02-16 20:32:35
【问题描述】:

您好,我有一大堆 .mp3 文件,我想与 NSFileManager 一起使用并存储在文档文件夹中。有没有办法可以在线下载 .mp3 文件,然后将其保存到文档文件夹中?这是我用于本地文件的内容。

let filemanager = NSFileManager.defaultManager()
let documentsPath : AnyObject = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask,true)[0]
let destinationPath:NSString = documentsPath.stringByAppendingString("/Attention.mp3")

if (!filemanager.fileExistsAtPath(destinationPath)) {
  var theError: NSError?
  let fileForCopy = NSBundle.mainBundle().pathForResource("Attention",ofType:"mp3")
  filemanager.copyItemAtPath(fileForCopy!,toPath:destinationPath, error: &theError)

  if (theError == nil) {
    println("The music files has been saved.")
  } else {
    println("Error")
  }
} else {
  println("The files already exist")
}

【问题讨论】:

  • 如何指定要下载的文件?
  • @ThomasKilian 在本地,你可以这样做 let fileForCopy = NSBundle.mainBundle().pathForResource("Attention",ofType:"mp3")
  • @ThomasKilian 但是如果我有一个 URL 并想从那里下载它们呢?

标签: swift nsfilemanager


【解决方案1】:

我发现@leo-dabus 可以立即使用,但必须根据我的需要进行两个小改动。这可能对其他人有帮助。

更改 #1:处理包含在路径扩展中的文件名

    if let fileName = fileName {
        if fileName.hasSuffix(self.pathExtension) {
            destination = directory
                .appendingPathComponent(fileName)
        } else {
            destination = directory
                .appendingPathComponent(fileName)
                .appendingPathExtension(self.pathExtension)
        }
    } else {
        destination = directory
            .appendingPathComponent(lastPathComponent)
    }

更改 #2:如果目标文件存在,则生成唯一名称

例如生成File (2).txt 以避免覆盖File.txt,就像网络浏览器一样。

    if !overwrite {
        let pathExtension = destination.pathExtension
        let lastComponent = destination.deletingPathExtension().lastPathComponent
        var copyNumber = 2
        var attemptedURL = destination
        while FileManager.default.fileExists(atPath: attemptedURL.path) {
            attemptedURL = destination
                .deletingPathExtension()
                .deletingLastPathComponent()
                .appendingPathComponent("\(lastComponent) (\(copyNumber))")
                .appendingPathExtension(pathExtension)
            copyNumber += 1
        }
        destination = attemptedURL
    }

【讨论】:

    【解决方案2】:

    编辑/更新:Xcode 11.5 • Swift 5.2

    import UIKit
    import AVFoundation
    
    class ViewController: UIViewController {
        var player: AVPlayer!
        override func viewDidLoad() {
            super.viewDidLoad()
            let alarm = URL(string: "https://www.ringtonemobi.com/storage/upload/user_id_1/iphone-5-alarm-2016-08-21-01-49-25.mp3")!
            do {
                try alarm.download(to: .documentDirectory) { url, error in
                    guard let url = url else { return }
                    self.player = AVPlayer(url: url)
                    self.player.play()
                }
            } catch {
                print(error)
            }
        }
    }
    

    import Foundation
    extension URL {
        func download(to directory: FileManager.SearchPathDirectory, using fileName: String? = nil, overwrite: Bool = false, completion: @escaping (URL?, Error?) -> Void) throws {
            let directory = try FileManager.default.url(for: directory, in: .userDomainMask, appropriateFor: nil, create: true)
            let destination: URL
            if let fileName = fileName {
                destination = directory
                    .appendingPathComponent(fileName)
                    .appendingPathExtension(self.pathExtension)
            } else {
                destination = directory
                .appendingPathComponent(lastPathComponent)
            }
            if !overwrite, FileManager.default.fileExists(atPath: destination.path) {
                completion(destination, nil)
                return
            }
            URLSession.shared.downloadTask(with: self) { location, _, error in
                guard let location = location else {
                    completion(nil, error)
                    return
                }
                do {
                    if overwrite, FileManager.default.fileExists(atPath: destination.path) {
                        try FileManager.default.removeItem(at: destination)
                    }
                    try FileManager.default.moveItem(at: location, to: destination)
                    completion(destination, nil)
                } catch {
                    print(error)
                }
            }.resume()
        }
    }
    


    原答案

    Xcode 8.3.2 • Swift 3.1

    if let audioUrl = URL(string: "http://freetone.org/ring/stan/iPhone_5-Alarm.mp3") {
        // create your document folder url
        let documentsUrl = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
        // your destination file url
        let destination = documentsUrl.appendingPathComponent(audioUrl.lastPathComponent)
        print(destination)
        // check if it exists before downloading it
        if FileManager.default.fileExists(atPath: destination.path) {
            print("The file already exists at path")
        } else {
            //  if the file doesn't exist
            //  just download the data from your url
            URLSession.shared.downloadTask(with: audioUrl, completionHandler: { (location, response, error) in
                // after downloading your data you need to save it to your destination url
                guard
                    let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
                    let mimeType = response?.mimeType, mimeType.hasPrefix("audio"),
                    let location = location, error == nil
                    else { return }
                do {
                    try FileManager.default.moveItem(at: location, to: destination)
                    print("file saved")
                } catch {
                    print(error)
                }
            }).resume()
        }
    }
    

    【讨论】:

    • 如果我想使用本地文件。我已经知道该怎么做。我想知道如何通过链接下载 .mp3 并将其保存到下载文件夹。
    • 你太棒了,伙计!正是我想要的。只是一个简单的问题,如果我有多个链接怎么办?我可以将它们放入数组中吗?
    • 没问题!你帮了大忙。谢谢!
    【解决方案3】:

    Xcode 10.1、Swift 4

    我使用了上面来自@leo-dabus 的示例,但将代码分成了两个函数。我在这种方法中发现的一个缺陷是它没有处理文件已经下载的情况。

    此示例将删除任何先前已下载的文件并写入最新版本。

    /// Downloads a file asynchronously
    func loadFileAsync(url: URL, completion: @escaping (Bool) -> Void) {
    
        // create your document folder url
        let documentsUrl = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
    
        // your destination file url
        let destination = documentsUrl.appendingPathComponent(url.lastPathComponent)
    
        log.info(m: "downloading file from URL: \(url.absoluteString)")
        if FileManager().fileExists(atPath: destination.path) {
            print("The file already exists at path, deleting and replacing with latest")
    
            if FileManager().isDeletableFile(atPath: destination.path){
                do{
                    try FileManager().removeItem(at: destination)
                    print("previous file deleted")
                    self.saveFile(url: url, destination: destination) { (complete) in
                        if complete{
                            completion(true)
                        }else{
                            completion(false)
                        }
                    }
                }catch{
                    print("current file could not be deleted")
                }
            }
        // download the data from your url
        }else{
            self.saveFile(url: url, destination: destination) { (complete) in
                if complete{
                    completion(true)
                }else{
                    completion(false)
                }
            }
        }
    }
    
    
    func saveFile(url: URL, destination: URL, completion: @escaping (Bool) -> Void){
        URLSession.shared.downloadTask(with: url, completionHandler: { (location, response, error) in
            // after downloading your data you need to save it to your destination url
            guard
                let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
                let location = location, error == nil
                else { print("error with the url response"); completion(false); return}
            do {
                try FileManager.default.moveItem(at: location, to: destination)
                print("new file saved")
                completion(true)
            } catch {
                print("file could not be saved: \(error)")
                completion(false)
            }
        }).resume()
    }
    

    【讨论】:

    • 这个方法怎么用?
    • 不替换文件不是缺陷,实际上,他的代码确实处理了同名文件已经存在的情况;他选择跳过它,这是一个完全合理的解决方案。您的解决方案会删除现有文件,这可能不是正确的选择。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-28
    相关资源
    最近更新 更多