简体   繁体   中英

How do I download a file in Alamofire 4 and save to the Documents directory?

I'm trying to use Alamofire4 for Swift3 . I need to download . mp3 files and save them to the Documents directory. Current code is like so:

func downloadAudioFromURL(url: String, completion: ((_ status: ResponseStatus, _ audioLocalURL: URL?) -> Void)?) {


        let fileManager = FileManager.default
        let directoryURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
        let audioFullURL = String.ensureFullURLPath(url)


        alamoManager.download(audioFullURL)
            .validate { request, response, temporaryURL, destinationURL in

                var pathComponent = response.suggestedFilename!
                if pathComponent == "m4a.mp4" {
                    // Due to the old Android audio files without a filename
                    // Assign a unique name so audio files don't overwrite each other
                    pathComponent = "\(NSUUID().uuidString).mp4"
                }
                let localURL = directoryURL.appendingPathComponent(pathComponent)

                if response.statusCode == 200 {
                    completion?(.success, localURL)
                } else {
                    completion?(.failure, nil)
                }
                return .success
            }

            .responseJSON { response in
                debugPrint(response)
                print(response.temporaryURL)
                print(response.destinationURL)
        }
    }

However I can't actually access the files from the localURL after saving. I have also noticed that the localURL will be exactly the same for different files I try to download (maybe they are overwriting?). Eg: file:///Users/testuser/Library/Developer/CoreSimulator/Devices/D4254AEA-76DD-4F01-80AF-F1AF3BE8A204/data/Containers/Data/Application/29755154-DD21-4D4C-B340-6628607DC053/Documents/file1.mp3

Any ideas what I am doing wrong here?

Edited my code to look like this:

func downloadAudioFromURL(url: String, completion: ((_ status: ResponseStatus, _ audioLocalURL: URL?) -> Void)?) {

        let destination: DownloadRequest.DownloadFileDestination = { _, _ in
            var documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]

            documentsURL.appendPathComponent("Audiofile.mp3")
            return (documentsURL, [.removePreviousFile])
        }

        Alamofire.download(url, to: destination).response { response in

            if let localURL = response.destinationURL {

                completion?(.success, localURL)

            } else {

                completion?(.failure, nil)
            }

        }
}

How would I check for m4a.mp4 though?

Why are you performing .validate ? You are not storing any data after download in your current code. Alamofire allows you to store a file directly after download:

let destination: DownloadRequest.DownloadFileDestination = { _, _ in
    let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    let fileURL = documentsURL.appendPathComponent("pig.png")

    return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}

Alamofire.download(urlString, to: destination).response { response in
    print(response)

    if response.result.isSuccess, let imagePath = response.destinationURL?.path {
        let image = UIImage(contentsOfFile: imagePath)
    }
}

And by the way, the download path you are providing in the download method, is the local URL to the Documents directory and not a URL of a server.

Swift 3.x and Alamofire 4.x version

Well certainly the Alamofire example posted by Alamofire itself has bugs. Since fileURL returns Void it can't be used as a parameter in return statement.

Also remove .createIntermediateDirectories from option list of return statement if you don't want any Directories for the file you downloaded

EDIT
If you want to know the type of file, simply grab the last component part and convert String to NSString as NSString have these functionalities.

//audioUrl should be of type URL
let audioFileName = String((audioUrl?.lastPathComponent)!) as NSString

//path extension will consist of the type of file it is, m4a or mp4
let pathExtension = audioFileName.pathExtension

let destination: DownloadRequest.DownloadFileDestination = { _, _ in
    var documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]

    // the name of the file here I kept is yourFileName with appended extension
    documentsURL.appendPathComponent("yourFileName."+pathExtension)
    return (documentsURL, [.removePreviousFile])
}

Alamofire.download("yourAudioUrl", to: destination).response { response in
            if response.destinationURL != nil {
                print(response.destinationURL!)
            }
        }

Output is

file:///Users/rajan/Library/Developer/CoreSimulator/Devices/92B4AB6E-92C0-4864-916F-9CB8F9443014/data/Containers/Data/Application/781AA5AC-9BE7-46BB-8DD9-564BBB343F3B/Documents/yourFileName.mp3

which is the actual path of your file where it is stored.

Swift 5 - Alamofire 5.1. This is the approach.

let destination: DownloadRequest.Destination = { _, _ in
        let documentsURL = FileManager.default.urls(for: .picturesDirectory, in: .userDomainMask)[0]
            let fileURL = documentsURL.appendingPathComponent("image.png")

            return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
    }

    AF.download("https://httpbin.org/image/png", to: destination).response { response in
        debugPrint(response)

        if response.error == nil, let imagePath = response.fileURL?.path {
            let image = UIImage(contentsOfFile: imagePath)
        }
    }

Objective: Downloaded files from the server like gif, pdf, or zip will be store inside your specified folder name.

If you want to store your own folder structure like the name is "ZipFiles"

call .

self downloadZipFileFromServer(downloadFolderName: "ZipFiles");

Downloaded zip data is stored inside the document/ZiFiles/abc.zip

this just creates a folder inside the document

func createFolder(folderName:String)

Alamofire 4
Swift 4
/******Download image/zip/pdf  from the server and save in specific Dir********/
func downloadZipFileFromServer(downloadFolderName: string)
{
    let destination: DownloadRequest.DownloadFileDestination = { _, _ in
        var fileURL = self.createFolder(folderName: downloadFolderName)
        let fileName = URL(string : "www.xymob.com/abc.zip")
        fileURL = fileURL.appendingPathComponent((fileName?.lastPathComponent)!)
        return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
    }
    Alamofire.download("www.xymob.com/abc.zip", to: destination).response(completionHandler: { (DefaultDownloadResponse) in                
        print("res ",DefaultDownloadResponse.destinationURL!);
    })
}        

func createFolder(folderName:String)->URL
{
    var paths: [Any] = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
    let documentsDirectory: String = paths[0] as? String ?? ""
    let dataPath: String = URL(fileURLWithPath: documentsDirectory).appendingPathComponent(folderName).absoluteString
    if !FileManager.default.fileExists(atPath: dataPath) {
        try? FileManager.default.createDirectory(atPath: dataPath, withIntermediateDirectories: false, attributes: nil)
    }
    let fileURL = URL(string: dataPath)
    return fileURL!
}

Although the question is an older one,
I have re-written and tested it on Swift 5

import Foundation
import Alamofire

class DownloadFileService {
    
    static func downloadFile(using url: URL, completion: @escaping () -> Void) {
        let fileName = url.lastPathComponent
        
        let destination: DownloadRequest.Destination = { _, _ in
            var documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
            
            documentsURL.appendPathComponent(fileName)
            return (documentsURL, [.removePreviousFile])
        }
        
        AF.download(url, to: destination).response { response in
            print(response)
            completion()
        }
    }
    
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM