I use this alamofire request to get a pdf file, i want to save it as NSData:
func makeDataCall(urlString: String, completionHandler: (responseObject: NSData?, error: NSError?) -> ()) {
//Perform request
Alamofire.request(.GET, urlString, headers: ["Authorization": auth])
.responseData { request, response, responseData in
print(request)
print(response)
print(responseData)
completionHandler(responseObject: responseData.data, error: nil)
}
}
In the response i get this:
"Content-Length" = 592783;
"Content-Type" = "application/pdf";
However responseData.data is nil.
What am i doing wrong?
Editing my previous response, I read your question too quickly.
To download a file like a pdf you should use Alamofire.download rather than request.
There's a section on it in the docs:
https://github.com/Alamofire/Alamofire#downloading-a-file
just checked with some random pdf from the internet and this works for me just fine:
let destination = Alamofire.Request.suggestedDownloadDestination(directory: .DocumentDirectory, domain: .UserDomainMask)
Alamofire.download(.GET, "http://box2d.org/manual.pdf", destination: destination)
.response { _, _, _, error in
if let error = error {
print("Failed with error: \(error)")
} else {
print("Downloaded file successfully")
}
}
In Swift 4 use this code if you want to download a pdf
let h: HTTPHeaders = [
"Accept": "application/pdf",
"Content-Type": "application/pdf",
]
//random document name
let randomString = NSUUID().uuidString
let destination: DownloadRequest.DownloadFileDestination = { _, _ in
var documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
documentsURL.appendPathComponent("\(randomString).pdf")
return (documentsURL, [.removePreviousFile])
}
Alamofire.download("yourURL",method: .get,headers: h, to: destination).response { response in
if let destinationUrl = response.destinationURL {
print("destinationUrl \(destinationUrl.absoluteURL)")
}
}
Related
I am trying to mimic the following curl:
curl -v -F file=#/Users/myuser/Downloads/shelly-homekit-Shelly25.zip http://10.0.1.7/update
to use the curl command I downloaded the zip file and saved it to my computer.
My app should download the zip file, store it to the device and upload it to the server.
I tried both uploading it as a file and as Data with no success:
let destination: DownloadRequest.Destination = { _, _ in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendingPathComponent("shelly-homekit-Shelly1PM.zip")
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
AF.download("https://rojer.me/files/shelly/shelly-homekit-Shelly1PM.zip", to: destination).response { response in
debugPrint(response)
if response.error == nil, let zipPath = response.fileURL?.path {
let url = URL(string: zipPath)!
let headers: HTTPHeaders = [
.contentType("multipart/form-data")
]
if let data = try? Data(contentsOf: url) {
AF.upload(data, to: "http://"+dev.ipAddress+"/update",headers: headers).responseDecodable(of: HTTPBinResponse.self) { response in
debugPrint(response)
}
}
}
}
I get the following error:
Thank you for your help
Like mentioned in the comments you meed to allow arbitrary loads. See Transport security has blocked a cleartext HTTP. For more details
This didn't help:
Like mentioned in the comments you meed to allow arbitrary loads. See Transport security has blocked a cleartext HTTP. For more details
I set the permission as mentioned in the link:
Permission
But getting the following error:
Error
I also made some changes to my code:
let destination: DownloadRequest.Destination = { _, _ in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendingPathComponent("shelly-homekit-Shelly1PM.zip")
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
AF.download("https://rojer.me/files/shelly/shelly-homekit-Shelly1PM.zip", to: destination).response { response in
//debugPrint(response)
if response.error == nil {
AF.upload(response.fileURL!, to: "http://"+dev.ipAddress+"/update").responseDecodable(of: HTTPBinResponse.self) { response_up in
debugPrint(response_up)
}
}
}
I used Wireshark to sniff the request sent by the curl command:
POST /update HTTP/1.1
Host: 10.0.1.10
User-Agent: curl/7.64.1
Accept: */*
Content-Length: 988427
Content-Type: multipart/form-data; boundary=------------------------c67b500e63a02f3d
Expect: 100-continue
--------------------------c67b500e63a02f3d
Content-Disposition: form-data; name="file"; filename="shelly-homekit-Shelly25.zip"
Content-Type: application/octet-stream
Not sure how to translate it to AF code and add the fileData to the request, it should be a POST request and I believe the following code create the right http body:
let boundary = "Boundary-\(UUID().uuidString)"
let headers: HTTPHeaders = [
.contentType("multipart/form-data; boundary=\(boundary)")
]
let parameters = [
[
"key": "file",
"value": "shelly-homekit-Shelly25.zip",
"type": "application/octet-stream",
"src":response.fileURL!.path
]] as [[String : Any]]
var body = ""
for param in parameters {
let paramName = param["key"]!
body += "--\(boundary)\r\n"
body += "Content-Disposition:form-data; name=\"\(paramName)\""
body += "\r\nContent-Type: \(param["contentType"] as! String)"
//let paramSrc = param["src"] as! String
let paramValue = param["value"] as! String
let paramType = param["type"] as! String
//let fileData = try? NSData(contentsOfFile:paramSrc, options:[]) as Data
body += "; filename=\"\(paramValue)\"\r\n" + "Content-Type: " + paramType
}
let postData = body.data(using: .utf8)
The following code works:
let data = try? NSData(contentsOf: URL(string:str)!) as Data
AF.upload(multipartFormData: { multipartFormData in
multipartFormData.append(data!,withName: "file",fileName: file_name ,mimeType: "application/octet-stream")
}, to: "http://"+dev.ipAddress+"/update")
.response { response in
if response.response?.statusCode != 200 {
devicesUpdateError.append(dev.name)
}
}
I have successfully Implemented downloading mechanism for pdf files. Files are downloading and save to document directory of simulator.
Issue is when I try to disconnect internet the files locally stored not accessible. In my application I have two modes Online and Offline. In Online mode I store data and its accessible at Offline mode. Only problem is getting with pdf files.
How to access pdf files in offline mode?
viewController Code:
let fileType = (URL != nil) ? ".pdf" : ".png"
DataProvider.main.downloadFile(url: String(describing: self.URL!), filetype: fileType, callback: { success, response in
if !success || response == nil {
return false
}
if((response as! String).hasSuffix("pdf")){
self.imageScrollView.isHidden = true
self.contentContainer.isHidden = false
self.document = ReaderDocument(filePath:(response as! String).removingPercentEncoding, password: nil)
self.loadDocument()
self._contentView.removeFromSuperview()
self._contentView.frame = self.contentContainer.bounds
self.contentContainer.addSubview(self._contentView)
}else if (FileManager.default.fileExists(atPath: (response as! String).removingPercentEncoding!) ){
self.imageScrollView.isHidden = false
self.contentContainer.isHidden = true
let image = UIImage(contentsOfFile:(response as! String).removingPercentEncoding!)!
self.imageScrollView.display(image)
}
return true
})
DataProvider Class with custom directory:
#objc public func downloadFile(url:String, filetype: String, callback:#escaping (_ success:Bool, _ result:Any?)->(Bool)) -> Void {
var destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)
if filetype.elementsEqual(".pdf"){
destination = { _, _ in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendingPathComponent(String(abs(url.hashValue)) + ".pdf") //giving different name to file
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
}
Alamofire.download(
url,
method: .get,
parameters: nil,
encoding: JSONEncoding.default,
headers: [ "Accept":"*/*", "Authorization":"Bearer \(token ?? "")"],
to: destination).downloadProgress(closure: { (progress) in
//progress closure
print(progress)
}).response(completionHandler: { (DefaultDownloadResponse) in
//here you able to access the DefaultDownloadResponse
//result closure
callback(DefaultDownloadResponse.response?.statusCode == 200, DefaultDownloadResponse.destinationURL?.absoluteString.replacingOccurrences(of: "file://", with: ""))
print(DefaultDownloadResponse)
})
}
DefaultDownloadResponse.destinationURL when Online downloaded
▿ destinationURL : Optional<URL>
▿ some : file:///Users/macuser/Library/Developer/CoreSimulator/Devices/4B6C2727-46AB-4797-8CBB-D5A897867013/data/Containers/Data/Application/08441079-027E-4A5C-A504-4151A8405CF0/Documents/660444321507407187.pdf
- _url : file:///Users/macuser/Library/Developer/CoreSimulator/Devices/4B6C2727-46AB-4797-8CBB-D5A897867013/data/Containers/Data/Application/08441079-027E-4A5C-A504-4151A8405CF0/Documents/660444321507407187.pdf
when Offline its not downloaded or not picked from local
finished with error [-1009] Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline." UserInfo={_kCFStreamErrorCodeKey=50, NSUnderlyingError=0x600002fd50e0 {Error Domain=kCFErrorDomainCFNetwork Code=-1009 "(null)" UserInfo={_kCFStreamErrorCodeKey=50, _kCFStreamErrorDomainKey=1}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDownloadTask <98102CE1-D70F-4A0E-AB89-8CAAEFE29213>.<13>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDownloadTask <98102CE1-D70F-4A0E-AB89-8CAAEFE29213>.<13>"
), NSLocalizedDescription=The Internet connection appears to be offline., NSErrorFailingURLStringKey=http://pdfv2.zta.com/Pdf/GetPageFromPdfUrl?url=http://d4static.zed.com/31/2019/8585/6999605/6999605.pdf&page=1,
Here I am checking if file exists or not first.
//Call it like this
checkIfFileExists(urlString: "your_url")
func checkIfFileExists(urlString: String) {
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = NSURL(fileURLWithPath: path)
let fileName = urlString.fileName()
let fileManager = FileManager.default
let filePath = url.appendingPathComponent("\(fileName).pdf")?.path
print("filePath : \(String(describing: filePath))")
if fileManager.fileExists(atPath: filePath!) {
print("File exists")
} else {
print("File doesn't exists")
stackoverFlowTask()
}
}
func stackoverFlowTask() {
downloadFile(url: "your_url", filetype: ".pdf", callback: { success, response in
if !success || response == nil {
return false
}
return true
})
}
public func downloadFile(url:String, filetype: String, callback:#escaping (_ success:Bool, _ result:Any?)->(Bool)) -> Void {
var destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)
if filetype.elementsEqual(".pdf"){
destination = { _, _ in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let downloadFileName = url.fileName()
let fileURL = documentsURL.appendingPathComponent("\(downloadFileName).pdf")
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
}
Alamofire.download(
url,
method: .get,
parameters: nil,
encoding: JSONEncoding.default,
headers: nil,
to: destination).downloadProgress(closure: { (progress) in
//progress closure
print(progress)
}).response(completionHandler: { (DefaultDownloadResponse) in
//here you able to access the DefaultDownloadResponse
//result closure
callback(DefaultDownloadResponse.response?.statusCode == 200, DefaultDownloadResponse.destinationURL?.absoluteString.replacingOccurrences(of: "file://", with: ""))
print(DefaultDownloadResponse)
})
}
Extension:
extension String {
func fileName() -> String {
return self.replacingOccurrences(of: "[/+.:.-=.%&]", with: "", options: .regularExpression, range: nil)
}
}
First of all, after successfully download from the URL using Alamofire, I am changing the file extension to.ZIP, then getting an error while unzipping.
Not getting expected file.
let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)
Alamofire.download(fileURL!, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: headers, to: destination).downloadProgress(closure: { (progress) in
print(progress.completedUnitCount)
}).responseData { (responce) in
let destiUrl = responce.destinationURL
print(destiUrl!)
let newUrl = destiUrl?.deletingPathExtension().appendingPathExtension("zip")
do {
try FileManager.default.copyItem(at: destiUrl!, to: newUrl!)
let unzipDirectory = try Zip.quickUnzipFile(newUrl!)
print(unzipDirectory.absoluteString)
}
catch let error as NSError{
print(error)
}
}
File URL after successful download-->
file:///var/mobile/Containers/Data/Application/9D96958C-903E-4693-9965-6FB919BB24F1/Documents/'87dc4a8ddce24cf9ad35a251d6a98195.hub'
File URL after converting to .zip
file:///var/mobile/Containers/Data/Application/9D96958C-903E-4693-9965-6FB919BB24F1/Documents/'87dc4a8ddce24cf9ad35a251d6a98195.zip
Final url after unzipping
file:///var/mobile/Containers/Data/Application/9D96958C-903E-4693-9965-6FB919BB24F1/Documents/'87dc4a8ddce24cf9ad35a251d6a98195/
Actual result should be audio file.
Tried replacing the name of the file at the time of successfully download using below code. -->
func saveFileInDocDirectory(data: Data?, fileName: String?, successblock: #escaping (_ path: String?) -> Void) { // To add the image to cache for given identifier.
let paths = NSSearchPathForDirectoriesInDomains( .documentDirectory, .userDomainMask, true)[0] as String
let path = paths.appending("/\(fileName!)")
if (FileManager.default.fileExists(atPath: path)) {
try! FileManager.default.removeItem(atPath: path)
} else {
do {
try data?.write(to: URL(fileURLWithPath: path, isDirectory: false))
successblock(path)
} catch {
successblock(nil)
print("Error while caching the data in cache folder.")
}
}
}
And after that unzipped using SSZipArchive library in Alamofire download function -->
Alamofire.download(fileURL!, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: headers, to: destination).downloadProgress(closure: { (progress) in
print(progress.completedUnitCount)
}).responseData { (responce) in
let destiUrl = responce.destinationURL
print(destiUrl!)
let name = destiUrl?.deletingPathExtension().lastPathComponent
self.saveFileInDocDirectory(data: responce.result.value, fileName: "\(name!).zip", successblock: { (path) in
print(path!)
var filepath = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)[0]
let url = URL(fileURLWithPath: filepath)
do {
try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
let done = SSZipArchive.unzipFile(atPath: path!, toDestination: url.path)
if done{
let items = try FileManager.default.contentsOfDirectory(atPath: url.path)
print(items)
let destinationUrl = url.appendingPathComponent(items[0])
print(destinationUrl)
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
player = AVQueuePlayer(url: destinationUrl)
player.play()
}
} catch let error as NSError{
print(error)
}
})
}
I'm using this function to download videos to a file called downloads using alamofire.
How would I edit it so it saves videos to the camera roll
func downloadVideoToCameraRoll() {
let destination: DownloadRequest.DownloadFileDestination = { _, response in
let pathComponent = response.suggestedFilename!
var documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let directoryURL: URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let folderPath: URL = directoryURL.appendingPathComponent("Downloads", isDirectory: true)
let fileURL: URL = folderPath.appendingPathComponent(pathComponent)
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
Alamofire.download(firstId, method: .get, parameters: nil, encoding: JSONEncoding.default, to: destination)
.downloadProgress(queue: DispatchQueue.global(qos: .utility)) { progress in
self.progresss.setProgress(Float(progress.fractionCompleted), animated: true)
//print("Progress: \(progress.fractionCompleted)")
}
.validate { request, response, temporaryURL, destinationURL in
// Custom evaluation closure now includes file URLs (allows you to parse out error messages if necessary)
return .success
}
.responseJSON { response in
debugPrint(response)
print(response.temporaryURL!)
print(response.destinationURL!)
}
You can use this in swift 3:
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: urlToYourVideo)
}) { saved, error in
if saved {
print("Saved")
}
}
Noted: Need to import Photos
Try the code below
func downloadVideoToCameraRoll() {
let destination: DownloadRequest.DownloadFileDestination = { _, response in
let pathComponent = response.suggestedFilename!
var documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let directoryURL: URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
return (directoryURL, [.removePreviousFile, .createIntermediateDirectories])
}
Alamofire.download(firstId, method: .get, parameters: nil, encoding: JSONEncoding.default, to: destination)
.downloadProgress(queue: DispatchQueue.global(qos: .utility)) { progress in
self.progresss.setProgress(Float(progress.fractionCompleted), animated: true)
//print("Progress: \(progress.fractionCompleted)")
}
.validate { request, response, temporaryURL, destinationURL in
// Custom evaluation closure now includes file URLs (allows you to parse out error messages if necessary)
return .success
}
.responseJSON { response in
debugPrint(response)
print(response.temporaryURL!)
print(response.destinationURL!)
saveVideoTo(destinationURL)
}
func saveVideoTo(_ videoUrl:Url?){
if videoUrl != nil {
PHPhotoLibrary.sharedPhotoLibrary().performChanges({ () -> Void in
let createAssetRequest: PHAssetChangeRequest = PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL(NSURL(string: videoUrl)!)!
createAssetRequest.placeholderForCreatedAsset
}) { (success, error) -> Void in
if success {
//saved successfully
}
else {
//error occured
}
}
}
}
In older version of Alamofire. This is how I download file
let destinationPath = Alamofire.Request.suggestedDownloadDestination( directory: .documentDirectory, domain: .userDomainMask);
Alamofire.download(.GET, urlString, destination: destinationPath)
.progress { bytesRead, totalBytesRead, totalBytesExpectedToRead in
// print(totalBytesRead)
}
.response { request, response, _, error in
let downloadedFilePath = destinationPath(URL(string: "")!, response!);
NSUserDefaultsHelper.saveURL(downloadedFilePath, key: urlString);
completion(downloadedFilePath, true);
}
But now in the new version, my code is completely unusable and there is no similar function in the Alamofire library.
Any ideas please?
I used to use this statements:
let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)
Alamofire.download(
url,
method: .get,
parameters: parameters,
encoding: JSONEncoding.default,
headers: nil,
to: destination).downloadProgress(closure: { (progress) in
//progress closure
}).response(completionHandler: { (DefaultDownloadResponse) in
//here you able to access the DefaultDownloadResponse
//result closure
})
For more details read more in Alamofire docs about Migration to 4.0:
Swift 4.0
let destination: DownloadRequest.DownloadFileDestination = { _, _ in
var documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
documentsURL.appendPathComponent("file.csv")
return (documentsURL, [.removePreviousFile])
}
Alamofire.download(url, to: destination).responseData { response in
if let destinationUrl = response.destinationURL {
print("destinationUrl \(destinationUrl.absoluteURL)")
}
}
There are several enhancements in Alamofire 4. The first of which is the optionality of the destination closure. Now, by default, the destination closure is nil which means the file is not moved anywhere on the file system and the temporary URL is returned.
This is the default execution:-
Alamofire.download(urlString).responseData { response in
print("Temporary URL: \(response.temporaryURL)")
}
This is my code to download file with Alamofire 4.0 which return destination Url of file:-
let destination: DownloadRequest.DownloadFileDestination = { _, _ in
var documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
documentsURL.appendPathComponent("duck.png")
return (documentsURL, [.removePreviousFile])
}
Alamofire.download(url, to: destination).responseData { response in
if let destinationUrl = response.destinationURL ? {
completionHandler(destinationUrl)
}
}
Downloading mp3 file with Alamofire 4.0 Swift 4.x
Since almost all samples seems to be about downloading an image or a JSON file, it took me hours to find the right solution.
I will share it here hoping it would help others to save some time.
func startDownload(audioUrl:String) -> Void {
let fileUrl = self.getSaveFileUrl(fileName: audioUrl)
let destination: DownloadRequest.DownloadFileDestination = { _, _ in
return (fileUrl, [.removePreviousFile, .createIntermediateDirectories])
}
Alamofire.download(audioUrl, to:destination)
.downloadProgress { (progress) in
self.progressLabel.text = (String)(progress.fractionCompleted)
}
.responseData { (data) in
self.progressLabel.text = "Completed!"
}
}
func getSaveFileUrl(fileName: String) -> URL {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let nameUrl = URL(string: fileName)
let fileURL = documentsURL.appendingPathComponent((nameUrl?.lastPathComponent)!)
NSLog(fileURL.absoluteString)
return fileURL;
}
For the latest versions this is how it should look like:
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)
}
}
Swift 3 Alamofire (4.4.0):
.plist add key "App Transport Security Settings->Allow Arbitrary Loads->Yes" if you copy and paste code below:
import Alamofire
let destination = DownloadRequest.suggestedDownloadDestination()
Alamofire.download("http://zmp3-mp3-lossless-te-zmp3-bdhcm-1.zadn.vn/151e407bb43f5d61042e/1223048424027738068?key=f-zMo3GZKlhVibnvGMsMuQ&expires=1495726053&filename=See%20You%20Again%20-%20Wiz%20Khalifa%20Charlie%20Puth%20(NhacPro.net).flac", to: destination).downloadProgress(queue: DispatchQueue.global(qos: .utility)) { (progress) in
print("Progress: \(progress.fractionCompleted)")
} .validate().responseData { ( response ) in
print(response.destinationURL!.lastPathComponent)
}
Use this code for download file
let fileManager = FileManager.default
let directoryURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
Alamofire.request(\(downloadUrl)).downloadProgress(closure : { (progress) in
print(progress.fractionCompleted)
}).responseData{ (response) in
print(response)
print(response.result.value!)
print(response.result.description)
let randomString = NSUUID().uuidString
if let data = response.result.value {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let videoURL = documentsURL.appendingPathComponent("\(randomString)")
do {
try data.write(to: videoURL)
} catch {
print("Something went wrong!")
}
}
}