I created a route on my nodejs server that sends a zip file of images. But unfortunately every time I make a request to the server I am able to download the zip file to but I can seem to be able to unzip file. I get the error message "unarchivable" and localized description of the error message is "The operation couldn’t be completed. (Zipper.Zipper.ArchiveError error 0.)" The nodejs route works perfectly fine with postman and my Android app.
Alamofire: 5.0.0-beta.5
swift: 4.2
I found this
Unzipping Error The operation couldn’t be completed. (Zip.ZipError error 1.) between ViewControllers
but I couldn't make sense it. I did try using a completion handler for the download process but still got the same error message
// swift code
do {
try FileManager.default.createDirectory(at: self.getDocumentsDirectory().appendingPathComponent("zipServe"), withIntermediateDirectories: true, attributes: nil)
let destination: DownloadRequest.Destination = { _, _ in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendingPathComponent("zipServe").appendingPathComponent("pig.zip")
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
AF.download(self.main_url + "imagesAll/shop/", method: HTTPMethod.post, parameters: parameters, headers: headers, to: destination)
.downloadProgress { progress in
print("Download Progress: \(progress.isFinished)")
}
.response { response in
if response.error == nil, let imagePath = response.fileURL {
do {
print(imagePath)
if FileManager.default.fileExists(atPath: imagePath.path) {
print("File exists")
try FileManager().unzip(item: imagePath.absoluteURL, to: self.getDocumentsDirectory().appendingPathComponent("zipServe"))
} else {
print("File not found")
}
} catch let error {
print(error.localizedDescription)
}
}
}
} catch let error as NSError {
print(error.localizedDescription)
}
I should be able to see a list of the images contained within the zip file.
Related
In a different question I asked how to save files on a directory of the user's choosing. The reply was the following code, which works great.
func resolveURL(for key: String) throws -> URL {
if let data = UserDefaults.standard.data(forKey: key) {
var isStale = false
let url = try URL(resolvingBookmarkData: data, options:[.withSecurityScope], bookmarkDataIsStale: &isStale)
if isStale {
let newData = try url.bookmarkData(options: [.withSecurityScope])
UserDefaults.standard.set(newData, forKey: key)
}
return url
} else {
let panel = NSOpenPanel()
panel.allowsMultipleSelection = false
panel.canChooseDirectories = true
panel.canCreateDirectories = true
panel.canChooseFiles = false
if panel.runModal() == .OK,
let url = panel.url {
let newData = try url.bookmarkData(options: [.withSecurityScope])
UserDefaults.standard.set(newData, forKey: key)
return url
} else {
throw ResolveError.cancelled
}
}
}
func saveFile(filename: String, contents: String) {
do {
let directoryURL = try resolveURL(for: "savedDirectory")
let documentURL = directoryURL.appendingPathComponent (filename + ".txt")
print("saving " + documentURL.absoluteString)
try directoryURL.accessSecurityScopedResource(at: documentURL) { url in
try contents.write (to: url, atomically: false, encoding: .utf8)
}
} catch let error as ResolveError {
print("Resolve error:", error)
} catch {
print(error)
}
}
Now, the next step is to go to the directory the user chose when the app loads, and if any files are there, ready each one and add the contents of those files to the struct I use to hold the data.
Googling a little bit I found that you can read all files in a directory using FileManager.default.contentsOfDirectory so I wrote:
func loadFiles() {
do {
let directoryURL = try resolveURL(for: "savedDirectory")
let contents = try FileManager.default.contentsOfDirectory(at: directoryURL,
includingPropertiesForKeys: nil,
options: [.skipsHiddenFiles])
for file in contents {
print(file.absoluteString)
}
} catch let error as ResolveError {
print("Resolve error:", error)
return
} catch {
print(error)
return
}
}
But, I get the following error:
Error Domain=NSCocoaErrorDomain Code=257 "The file “myFiles” couldn’t be opened because you don’t have permission to view it." UserInfo={NSURL=file:///Users/aleph/myFiles, NSFilePath=/Users/aleph/myFiles, NSUnderlyingError=0x600000704ba0 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
which looking at my code I would guess it's happening because I'm not using directoryURL.accessSecurityScopedResource. I tried to add that, or find any other way, but I'm running into a block and I don't know how to get to the directory saved in savedDirectory, and go through every file, reading its contents.
Thank you for any help
If I use:
directoryURL.startAccessingSecurityScopedResource()
// load the files
directoryURL.stopAccessingSecurityScopedResource()
Then it works.
I have written a download function in my app which downloads a .mp3 file from a url and saves it into Application Support directory locally.
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
if let url = downloadTask.originalRequest?.url {
let destinationURL = moveFileToSupportDirectory(source: location, fileName: url.lastPathComponent , subDir: "MyApp")
onDownloadComplete?(destinationURL, nil)
}
}
func moveFileToSupportDirectory(source: URL, fileName: String, subDir: String) -> URL? {
do {
let manager = FileManager.default
let directoryURL = try manager.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent(subDir)
let destination = directoryURL.appendingPathComponent(fileName)
do {
try FileManager.default.createDirectory (at: directoryURL, withIntermediateDirectories: true, attributes: nil)
} catch {
print("Error creating directory: \(error)")
return nil
}
if FileManager().fileExists(atPath: destination.path) {
print("File already exists [\(destination.path)]")
return destination
}
else {
print("Move from \(source.path) to destination \(destination.path)...")
try manager.moveItem(at: source, to: destination)
return destination
}
} catch {
printTrace("\(error)")
return nil
}
}
After that when user presses "Play" button, the app will retrieve the saved URL using fileName and play it locally.
The function of retrieving URL:
func getSavedURL(of name: String) -> URL? {
do {
let manager = FileManager.default
let directoryURL = try manager.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("MyApp")
let destination = directoryURL.appendingPathComponent(name)
if FileManager().fileExists(atPath: destination.path) {
print("File exists [\(destination.path)]")
let isReachable = try destination.checkResourceIsReachable()
if isReachable {
print("File is reachable \(destination.absoluteString)")
return destination
} else {
print("File is unreachable")
return nil
}
}
else {
print("File \(name) doesn't exist")
return nil
}
} catch {
print("\(error)")
return nil
}
}
The question is that when I download and play it in the same build, all works perfectly. However if I start another build and play the file saved previously, the audio player will throw following error:
ExtAudioFile.cpp:193:Open: about to throw 'wht?': open audio file
[avae] AVAEInternal.h:109 [AVAudioFile.mm:134:AVAudioFileImpl: (ExtAudioFileOpenURL((CFURLRef)fileURL, &_extAudioFile)): error 2003334207
From this thread the error seems to be related to nil of audio file:
OSStatus error 2003334207 when using AVAudioPlayer
But when retrieving the URL it has already passed all the checks (i.e. exists and is reachable) and return a non-nil URL. How come the audio file cannot be played properly even though it does exist and is reachable?
Is it related to this sandbox issue?
Document directory path change when rebuild application.
But I retrieve the URL using the relative method, I think this should not happen in my case.
p.s. I use SwiftAudioPlayer to play my audio file locally. It seems to use AudioEngine inside.
Im using this below method to download and save my video to gallery, with .mp4 it's work normally, but when change to .m3u8 it's always fail.
func downloadVideoLinkAndCreateAsset(_ videoLink: String,_ fileName : String) {
// use guard to make sure you have a valid url
guard let videoURL = URL(string: videoLink) else { return }
guard let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileNameToSave = "CiviX_HistoryVideo_\(fileName)"
// check if the file already exist at the destination folder if you don't want to download it twice
if !FileManager.default.fileExists(atPath: documentsDirectoryURL.appendingPathComponent(fileNameToSave).path) {
// set up your download task
URLSession.shared.downloadTask(with: videoURL) { (location, response, error) -> Void in
// use guard to unwrap your optional url
guard let location = location else { return }
// create a deatination url with the server response suggested file name
let destinationURL = documentsDirectoryURL.appendingPathComponent(fileNameToSave)
print("destinationURL: \(destinationURL)")
do {
try FileManager.default.moveItem(at: location, to: destinationURL)
PHPhotoLibrary.requestAuthorization({ (authorizationStatus: PHAuthorizationStatus) -> Void in
// check if user authorized access photos for your app
if authorizationStatus == .authorized {
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: destinationURL)}) { completed, error in
if completed {
print("Video asset created")
} else {
print("Video asset create failed: \(error?.localizedDescription)")
}
}
}
})
} catch { print("file manager error: \(error.localizedDescription)") }
}.resume()
} else {
print("File already exists at destination url")
}
}
then here is method to call
let urlString = response.replacingOccurrences(of: "\"", with: "") -> my m3u8 URL
let videoImageUrl = "https://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4" -> always success
//TEST MP4 file -> ALWAYS SUCCESS
self.downloadVideoLinkAndCreateAsset(videoImageUrl, "big_buck_bunny_720p_1mb.mp4")
//TEST M3U8 FIlE -> FAIL
self.downloadVideoLinkAndCreateAsset(urlString, history.fileName!) -> fileName format is 'abc.mp4'
The log result for MP4
destinationURL: file:///Users/thehe/Library/Developer/CoreSimulator/Devices/05C6DE76-6609-4E4A-B00D-2CE3622D2EF8/data/Containers/Data/Application/90994674-6C07-47F9-A880-D1A80CDA0C27/Documents/CiviX_HistoryVideo_big_buck_bunny_720p_1mb.mp4
-> Video asset created
THe log result for M3U8
self.downloadVideoLinkAndCreateAsset(urlString, history.fileName!)
destinationURL: file:///Users/thehe/Library/Developer/CoreSimulator/Devices/05C6DE76-6609-4E4A-B00D-2CE3622D2EF8/data/Containers/Data/Application/DA6ABC38-4E0A-44C7-9C56-8B65F1DC0D4D/Documents/CiviX_HistoryVideo_20-1-2019_3h18m32s.mp4
-> Video asset create failed: Optional("The operation couldn’t be completed. (Cocoa error -1.)")
I also tried to save with .m3u8 extension but still not working
self.downloadVideoLinkAndCreateAsset(urlString, "TEST_M3U8_FILE.m3u8")
destinationURL: file:///Users/thehe/Library/Developer/CoreSimulator/Devices/05C6DE76-6609-4E4A-B00D-2CE3622D2EF8/data/Containers/Data/Application/9B42A55B-4E3E-4A20-A0DC-6E1ED22471A2/Documents/CiviX_HistoryVideo_TEST_M3U8_FILE.m3u8
-> Video asset create failed: Optional("The operation couldn’t be completed. (Cocoa error -1.)")
M3U8 is an audio/video playlist file and it cannot be saved to the gallery.
the code i used for downloading the book is ..
//Create URL to the source file you want to download
let fileURL = URL(string:myFileURL)
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig)
let request = URLRequest(url:fileURL!)
let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
// Success
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Successfully downloaded. Status code: \(statusCode)")
DispatchQueue.main.async {
self.toast(msg: "Download completed")
self.activityIndicator.stopAnimating()
self.activityIndicator.isHidden = true
}
}
do {
try FileManager.default.copyItem(at: tempLocalUrl, to: destinationFileUrl)
} catch (let writeError) {
print("Error creating a file \(destinationFileUrl) : \(writeError)")
}
} else {
print("Error took place while downloading a file. Error description: %#", error?.localizedDescription);
}
after that i tried to unzip the epub file from the destinationFileUrl using the code
let status = SSZipArchive.unzipFile(atPath: destinationFileUrl , toDestination:destPath, delegate:self)
the status value returns false, so the output folder only contains META-INF folder with no items
How can i unzip the epub correctly
Please refer this -->> Not getting eject file after unzipping downloaded file URL
var filepath = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)[0]
filepath += "/\(UUID().uuidString)"
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)
}
} catch let error as NSError{
print(error)
}
I have got multiple audio files on server.
I want to download all audio files first, and then when all are downloaded, i need to play them one after another.
What will be the best approach to achieve this?
Thanks!
if let audioUrl = URL(string: "http://freetone.org/ring/stan/iPhone_5-Alarm.mp3") {
// then lets create your document folder url
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
// lets create your destination file url
let destinationUrl = documentsDirectoryURL.appendingPathComponent(audioUrl.lastPathComponent)
print(destinationUrl)
// to check if it exists before downloading it
if FileManager.default.fileExists(atPath: destinationUrl.path) {
print("The file already exists at path")
// if the file doesn't exist
} else {
// you can use NSURLSession.sharedSession to download the data asynchronously
URLSession.shared.downloadTask(with: audioUrl, completionHandler: { (location, response, error) -> Void in
guard let location = location, error == nil else { return }
do {
// after downloading your file you need to move it to your destination url
try FileManager.default.moveItem(at: location, to: destinationUrl)
print("File moved to documents folder")
} catch let error as NSError {
print(error.localizedDescription)
}
}).resume()
}
}