I am attempting to upload an image file(.jpg) and a pdf file(.pdf) at the ssme time using Alamofire. I keep getting the following error which indicates that the files that I am trying to upload does not exist, which in fact they do.
multipartEncodingFailed(Alamofire.AFError.MultipartEncodingFailureReason.bodyPartFileNotReachableWithError(atURL: file:///var/mobile/Containers/Data/Application/7FF74F06-CD6E-47D6-850E-45E768C00D97/Documents/tempImage_wb.jpg, error: Error Domain=NSCocoaErrorDomain Code=260 "The file “tempImage_wb.jpg” couldn’t be opened because there is no such file." UserInfo={NSURL=file:///var/mobile/Containers/Data/Application/7FF74F06-CD6E-47D6-850E-45E768C00D97/Documents/tempImage_wb.jpg, NSFilePath=/var/mobile/Containers/Data/Application/7FF74F06-CD6E-47D6-850E-45E768C00D97/Documents/tempImage_wb.jpg, NSUnderlyingError=0x1c4841020 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}))
My code for uploading the files:
// GET URL FOR IMAGE AND PDF FILES
guard
let imageURLString = UserDefaults.standard.string(forKey: "URL_IMAGE"),
let pdfURLString = UserDefaults.standard.string(forKey: "URL_PDF") else{return}
guard
let imgURL = URL.init(string: imageURLString),
let pdfURL = URL.init(string: pdfURLString) else{return}
var arrayURLToUpload: [URL] = []
arrayURLToUpload.append(imgURL)
arrayURLToUpload.append(pdfURL)
let sendParamters = ["user_id": "1", "hashTagArray": jsonArrayHashTags]
Alamofire.upload(
multipartFormData: { multipartFormData in
for(key, value) in sendParamters{
multipartFormData.append((value.data(using: .utf8)!), withName: key)
}
for fileURL in arrayURLToUpload{
print("fileURL: \(fileURL)")
multipartFormData.append(fileURL, withName: "file[]")
}
},
to: UPLOAD_URL,
encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.responseJSON { response in
debugPrint(response)
}
/**TRACK PROGRESS OF UPLOAD**/
upload.uploadProgress { progress in
print(progress.fractionCompleted)
}
/***/
case .failure(let encodingError):
print(encodingError)
}
}
)
console:
fileURL: file:///var/mobile/Containers/Data/Application/7FF74F06-CD6E-47D6-850E-45E768C00D97/Documents/tempImage_wb.jpg
fileURL: file:///var/mobile/Containers/Data/Application/7FF74F06-CD6E-47D6-850E-45E768C00D97/Documents/tempImagePDF.pdf
I know the files exist becuase when I retrieve one of the files (.pdf) and display it I am able to view the stored pdf file:
guard let urlString = UserDefaults.standard.string(forKey: "URL_PDF") else{return}
guard let pdfURL = URL.init(string: urlString) else{
print("no pdf URL")
return
}
print("pdfURL: \(pdfURL)")
guard let pdf = PDFDocument.init(url: pdfURL) else{
print("NO PDF DOCUMENT FOUND")
return
}
pdfPreview.document = pdf // CAN VIEW PDF FILE!!!
pdfPreview.autoScales = true
print("pdf document displayed!")
console:
pdfURL: file:///var/mobile/Containers/Data/Application/90ECD1AE-B9A5-46C9-AD30-C5D8D850A361/Documents/tempImagePDF.pdf
pdf document displayed!
How I generate my URLs:
// Create a URL to save PDF
func createPdfURL() -> URL {
let fileName = "tempImagePDF.pdf"
let documentsDirectories = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentDirectory = documentsDirectories.first!
let pdfPageURL = documentDirectory.appendingPathComponent("\(fileName)")
return pdfPageURL
}
Then I save the PDF document as follows:
// SAVE PDF TO LOCAL FILE SYSTEM
func savePdfToLocaFile(pdf: PDFDocument) -> Void {
// CREATE URL FOR PDF DOCUMENT
let pdfURL = createPdfURL()
print("PDF SAVED TO URL: \(pdfURL)")
self.pdfDocumentURL = pdfURL
pdf.write(to: pdfURL)
}
I have also tried the following to retrieve the files by reconstructing the URL from the file name before using it to upload the files:
// CONSTRUCT URL FROM FILE NAME
let imgFileName = "tempImage_wb.jpg"
let pdfFileName = "tempImagePDF.pdf"
var dir: URL!
do {
dir = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
} catch{
print("error re-creating url")
}
let imgURL = dir.appendingPathComponent(imgFileName)
let pdfURL = dir.appendingPathComponent(pdfFileName)
new console reading after implementing #vadian's suggestion:
PDF SAVED TO URL: file:///var/mobile/Containers/Data/Application/35C9D3EE-2D0C-4028-BCF3-2FE4581A0686/Documents/tempImagePDF.pdf
fileURL: file:///var/mobile/Containers/Data/Application/35C9D3EE-2D0C-4028-BCF3-2FE4581A0686/Documents/tempImagePDF.pdf
Please note the different application container identifier 7FF74F06-CD6E-47D6-850E-45E768C00D97 and 90ECD1AE-B9A5-46C9-AD30-C5D8D850A36.
Container locations change periodically therefore never save full paths pointing to the application container to UserDefaults.
Save only the file names and get the URL to the current document directory always with
let documentsFolderURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
Then append the file name with .appendingPathComponent
Note: The try! is safe because the document directory is always created by the framework.
Related
This portion of the code is supposed to download a txt file from a website. By running this code it is able to successfully download the file from the website and place it into the app's Documents directory. I am able to see where the file is stored because in the last couple of lines in the code it prints out the location of the file. However, I am not able to get the file name that was recently downloaded. The goal is for me to try to get the name of the file so that I can be able to open it and read from it. What are my options in approaching this? What am I missing in this block of code that is preventing me from getting the name of the file that was recently downloaded?
guard let url1 = URL(string: website) else { return }
//This portion of the code focuses on creating a download task with a completion handler
//Completion handler moves the downloaded file to the app's directory
let downloadTask = URLSession.shared.downloadTask(with: url1) {
urlOrNil, responseOrNil, errorOrNil in
// check for and handle errors:
// * errorOrNil should be nil
// * responseOrNil should be an HTTPURLResponse with statusCode in 200..<299
guard let fileURL = urlOrNil else { return }
do {
let documentsURL = try
FileManager.default.url(for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false)
let savedURL = documentsURL.appendingPathComponent(
fileURL.lastPathComponent)
try FileManager.default.moveItem(at: fileURL, to: savedURL)
} catch {
print ("file error: \(error)")
}
}
downloadTask.resume()
//If you want to receive progress updates as the download proceeds, you must use a delegate.
var urlSession = URLSession(configuration: .default, delegate: self as? URLSessionDelegate, delegateQueue: nil)
func startDownload(url1: URL) -> String? {
let downloadTask = urlSession.downloadTask(with: url1)
let fname = downloadTask.response?.suggestedFilename
downloadTask.resume()
return fname
//self.downloadTask = downloadTask
}
let name = startDownload(url1: url1)
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) as NSArray
let documentsDir = paths.firstObject as! String
print("Path to the Documents directory\n\(documentsDir)")
You can get the name of the file at a given path by initialising a URL object from the path:
let name = URL(fileURLWithPath: yourPath).lastPathComponent
This returns an optional string. The name will be automatically unescaped, so it will be human-readable (no percent encoding).
The filename should be available to you from your fileURL variable, as this is where you got the filename in order to save it:
let name = fileURL.lastPathComponent
i am implementing pdf preview in my Swift app so i have decided to use third party library for Preview PDF i am using below library
Please Check Library Here
so first i am download url and store to document directory and than i am displaying it but pdf not previewed below is my code
func downloadFileFromURL(url: String) {
if let audioUrl = URL(string: url) {
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let destinationUrl = documentsDirectoryURL.appendingPathComponent(audioUrl.lastPathComponent)
print(destinationUrl)
if FileManager.default.fileExists(atPath: destinationUrl.path) {
print("The file already exists at path")
print(destinationUrl)
let document = try! PDFDocument(filePath: destinationUrl.lastPathComponent, password: "")
self.collectionView.document = document
} else {
URLSession.shared.downloadTask(with: audioUrl, completionHandler: { (location, response, error) -> Void in
guard let location = location, error == nil else { return }
do {
try FileManager.default.moveItem(at: location, to: destinationUrl)
print(destinationUrl)
let document = try! PDFDocument(filePath: destinationUrl.lastPathComponent, password: "")
self.collectionView.document = document
print("File moved to documents folder")
} catch let error as NSError {
print(error.localizedDescription)
}
}).resume()
}
}
}
and inside viewDidLoad() i am implementing below code
downloadFileFromURL(url: "http://housedocs.house.gov/edlabor/AAHCA-BillText-071409.pdf")
but still pdf is not previewed can some tell me its the right way to preview pdf with UXMPdf
or suggest me best pdfviewer for Swift from which i can load pdf from URL
You have to specify the full path rather than the last path component.
And remove the ! inside a do - catch block.
let document = try PDFDocument(filePath: destinationUrl.path, password: "")
As the password parameter is unused I recommend to use the built-in initializer
let document = try PDFDocument(url: destinationUrl)
I have an app for iOS that uses metal to render obj files. I am trying to add functionality for users to insert the url of an obj file online and render that. I am using alamofire and am not sure how I will access the file once downloaded, since I won't know the file name.
let destination = DownloadRequest.suggestedDownloadDestination(for: .downloadsDirectory)
let modelUrl = URL(string: "https://drive.google.com/file/d/110KRnku3N_K_EIN-ZLYXK128zjMqxGLM/view?usp=sharing")
Alamofire.download(
modelUrl!,
method: .get,
parameters: Parameters.init(),
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
})
let file = try? String(contentsOf: URL(string: (NSSearchPathForDirectoriesInDomains(.downloadsDirectory, .userDomainMask, true)[0]))!)
I am also fairly certain my method for retrieving the file will not work, but i'm not sure how to search the documents directory for a specific file.
The files I have working are in the project as .obj files in xcode, and I simply use this.
let assetURL = Bundle.main.url(forResource: modelName, withExtension: "obj")
Bundle.main will not return files in document directory, it is use for the files you put in your main bundle (inside Xcode while development usually). You need to use FileManager to access files in document directory. You can use this function to search files in your document directory.
func getFilePathInDocuments(fileName:String) -> String {
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = URL(fileURLWithPath: path)
let fileManager = FileManager.default
let filePath = url.appendingPathComponent(fileName).path
if (fileManager.fileExists(atPath: filePath)) {
return filePath
}else{
return ""
}
}
This is how you call it:
let foundPath = getFilePathInDocuments(fileName: "fileName.obj")
Update:
You can give a fileName to Almofire and you will receive the downloaded URL too from it.
let destinationPath: DownloadRequest.DownloadFileDestination = { _, _ in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0];
let fileURL = documentsURL.appendingPathComponent("fileName")
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
Alamofire.download(url, to: destinationPath)
.downloadProgress { progress in
}
.responseData { response in
}
To get downloaded document directory URL use response.destinationURL.
Using Alamofire for downloading. File is being downloaded in app document folder, in attachment, there is .hub file.
I need to change .hub file to .zip than i need to unzip this file for audio file.
Code for downloading ->
func getAudioFileFromServer(url: String, uuid: String) {
let fileURL = URL(string: url)
var request = URLRequest(url:fileURL!)
request.setValue("myapikey", forHTTPHeaderField: "x-api-key")
let destination = DownloadRequest.suggestedDownloadDestination()
Alamofire.download(request, to: destination).validate().responseData { response in
debugPrint(response)
print(response.destinationURL!)
}
}
Response from server ->
file:///var/mobile/Containers/Data/Application/FC5F17C4-E8D3-4406-926A-97EB9447D87B/Documents/'bac6151ffbe74140a31408938c91fa33.hub'
To rename a file, use the function moveItem(atPath:toPath:) of FileManager
To unzip, the easiest way would be to integrate some zip library: https://github.com/ZipArchive/ZipArchive or https://github.com/marmelroy/Zip
After downloading the file change the extension to .zip with "saveFileInDirectory" method and with success block we can get the main file.
self.saveFileInDirectory(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)
}
} catch let error as NSError{
print(error)
}
})
func saveFileInDirectory(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.")
}
}}
I am downloading file from firebase. let say the request url is following
social-cam-storage/albm-72/owner-2/1484043313786.jpeg
i can download the file using the following code
func downloadFile(url : String) {
let storageR = FIRStorage.storage().reference(withPath: url)
let maxSize : Int64 = 3 * 1024 * 1024 // 3MB
storageR.data(withMaxSize: maxSize) { (data, error) in
if error != nil {
print(error.debugDescription)
return
}
print(data!)
}
}
Now i need to store this data maintaining the directory structure of the url
I have tried
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
print(FileManager.default.createFile(atPath: "\(documentsURL.absoluteString)/\(url)", contents: data!, attributes: nil))
but i am getting false
so how to fix this or is there any other way to save??
Have you tried something like this? :
If you have the exact path already as a string:
try? data.write(to: URL(fileURLWithPath: path), options: [.atomic])
If you need the path there are a few methods:
func saveFile() {
let filePath = getDocumentsURL().absoluteString.appending(path)
try? data.write(to: URL(fileURLWithPath: filePath), options: [.atomic])
}
func getDocumentsURL() -> URL {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
return documentsURL
}
You could also just try saving the filename, and then loading later when you need it:
func fileInDocumentsDirectory(_ filename: String) -> String {
let fileURL = getDocumentsURL().appendingPathComponent(filename)
return fileURL.path
}
// To save file
func saveFile(data: Data) {
let fileName:String = "createUniqueFileName"
let filePath = fileInDocumentsDirectory(fileName)
saveData(data, filePath)
}
// To load file with saved file name
func loadFile(fileName: String) {
if let loadedData = loadData(fileName) {
// Handle data however you wish
}
}
func saveData(_ data: Data, path: String ) {
try? data.write(to: URL(fileURLWithPath: path), options: [.atomic])
}
func loadData(_ path: String) -> Data? {
let data:Data? = try? Data(contentsOf: URL(fileURLWithPath: path))
return data
}
Have you tried using the built in "download to file" API in Firebase Storage?
// Create a reference to the file you want to download
let fileURL = storage.reference(withPath: url)
// Create local filesystem URL
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = ...
// Download to the local filesystem
let downloadTask = islandRef.write(toFile: fileURL) { url, error in
if let error = error {
// Uh-oh, an error occurred!
} else {
// Local file URL is returned
}
}