I am parsing XLXS file. when I execute code getting error
"Fatal error: XLSX file corrupted or does not exist".
I double checked test.xlsx file available inside project.what is wrong with the code. Any help will be appreicated
guard let file = XLSXFile(filepath: "./test.xlsx") else {
fatalError("XLSX file corrupted or does not exist")
}
do{
for path in try file.parseWorksheetPaths() {
let ws = try file.parseWorksheet(at: path)
for row in ws.data?.rows ?? [] {
for c in row.cells {
print(c)
}
}
}
}catch{
print("error", error)
}
filepath can't accessing from bundle directory, need to pass correct path:
guard let filePath = Bundle.main.path(forResource: "test", ofType: "xlsx", inDirectory: nil) else {
fatalError("XLSX file not exist")
}
guard let file = XLSXFile(filepath: filePath) else {
fatalError("XLSX file corrupted")
}
One more thing to need to check, while adding XLSX file to project it must added to target & copy to bundle directory:
You should get the filePath using the following :
if let path = Bundle.main.path(forResource: "test", ofType: "xlsx") {
//Use your file here
}
Try replacing
guard let file = XLSXFile(filepath: "./test.xlsx") else {
with
guard var file = XLSXFile(filepath: "./test.xlsx") else {
I do not have experience dealing with files but I hope this helps you
Related
I want to load a file from AppBundle. I am able to do this at top level ("/file.txt") with following code:
guard let path = Bundle.main.path(forResource: "file", ofType: "txt") else {
print("file not found")
return
}
But when I put the "file.txt" in a folder ("anyFolder") I do not know how to read the file from "/anyFolder/file.txt".
The shown code does not work with a nested structure.
You should use "url(forResource:withExtension:subdirectory:)"
Check Apple Document
guard let url = Bundle.main.url(forResource: "ios", withExtension: ".ovpn")
else{
print("error")
return
}
print("bundle",url.path)
Create urls of ios.file
I have an issues in changing the file path at every launch of the app.
I have a file("AppConstant.json") in application bundle, and this file I need to copy into application document directory. I am successfully saving "AppConstant.json" file inside the created user folder "MyFolder" on Document directory.
But the problem is when I relaunch the application second time, it's not showing the same path. Also I am using relativepath, but still it not getting.
here is the code
// calling the directory
let stringAppConstant = copyFileFromBundleToDocumentDirectory(resourceFile: "AppConstant", resourceExtension: "json")
// saving or get exit file path
func copyFileFromBundleToDocumentDirectory(resourceFile: String, resourceExtension: String) -> String
{
var stringURLPath = "Error_URLPath"
let fileManager = FileManager.default
let docURL = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let destFolderPath = URL(string:docURL)?.appendingPathComponent("MyFolder")
let fileName = "\(resourceFile).\(resourceExtension)"
guard let newDestPath = destFolderPath, let sourcePath = Bundle.main.path(forResource: resourceFile, ofType: ".\(resourceExtension)"), let fullDestPath = NSURL(fileURLWithPath: newDestPath.absoluteString).appendingPathComponent(fileName) else {
return stringURLPath
}
if !fileManager.fileExists(atPath: newDestPath.path) {
do {
try fileManager.createDirectory(atPath: newDestPath.path,withIntermediateDirectories: true, attributes: nil)
print("Created folder successfully in :::", newDestPath.path)
} catch {
print("Error in creating folder :::",error.localizedDescription);
}
}
else {
print("Folder is already exist!")
}
if fileManager.fileExists(atPath: fullDestPath.path) {
print("File is exist in ::: \(fullDestPath.path)")
stringURLPath = fullDestPath.path
}
else {
do {
try fileManager.copyItem(atPath: sourcePath, toPath: fullDestPath.path)
print("Saved file successfully in :::", fullDestPath.path)
stringURLPath = fullDestPath.path
} catch {
print("Error in creating file ::: \(error.localizedDescription)")
}
}
return stringURLPath
}
Please help me, where I need to save the path in Sandbox. Is this right way what I implemented.
I am running in device and simulator, both path are different while relaunch
this is the path for first time launch:
/var/mobile/Containers/Data/Application/81B568A7-0932-4C3E-91EB-9DD62416DFE8/Documents/MyFolder/AppConstant.json
relaunch the application I am getting new path:
/var/mobile/Containers/Data/Application/3DAABAC3-0DF5-415B-82A5-72B204311904/Documents/MyFolder/AppConstant.json
NOTE: I create a sample project and I use this same code and it's working. But in existing project it's not working. I am using the same bundle id and profile only for both sample and project. Checked the file added reference, settings, version all are same.
Any idea?
The behavior that the container path changes periodically is normal.
These lines
let destFolderPath = URL(string:docURL)?.appendingPathComponent("MyFolder")
let fileName = "\(resourceFile).\(resourceExtension)"
guard let newDestPath = destFolderPath, let sourcePath = Bundle.main.path(forResource: resourceFile, ofType: ".\(resourceExtension)"), let fullDestPath = NSURL(fileURLWithPath: newDestPath.absoluteString).appendingPathComponent(fileName) else {
return stringURLPath
}
contain a lot of mistakes
URL(string is the wrong API for file paths, it's URL(fileURLWithPath).
The second parameter of path(forResource:ofType:) must not have a leading dot.
The API absoluteString is wrong as parameter of URL(fileURLWithPath
Not a real mistake but don't use NSURL in Swift.
It's highly recommended to use always the URL related API to concatenate paths and get the documents folder from FileManager. Further it's good practice to make the method throw the real error rather than returning a meaningless literal string. And NSSearchPathForDirectoriesInDomains is outdated and should not be used in Swift.
func copyFileFromBundleToDocumentDirectory(resourceFile: String, resourceExtension: String) throws -> URL
{
let sourceURL = Bundle.main.url(forResource: resourceFile, withExtension: resourceExtension)!
let fileManager = FileManager.default
let destFolderURL = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent("MyFolder")
let fullDestURL = destFolderURL.appendingPathComponent(resourceFile).appendingPathExtension(resourceExtension)
if !fileManager.fileExists(atPath: destFolderURL.path) {
try fileManager.createDirectory(at: destFolderURL, withIntermediateDirectories: true, attributes: nil)
print("Created folder successfully in :::", destFolderURL.path)
try fileManager.copyItem(at: sourceURL, to: fullDestURL)
print("Saved file successfully in :::", fullDestURL.path)
} else {
print("Folder already exists!")
if fileManager.fileExists(atPath: fullDestURL.path) {
print("File exists in ::: \(fullDestURL.path)")
} else {
try fileManager.copyItem(at: sourceURL, to: fullDestURL)
print("Saved file successfully in :::", fullDestURL.path)
}
}
return fullDestURL
}
Edit 1:
Hi I created the new project and use the same code I posted in main, and it's working. But in the real project it not working.
Not sure what exactly going on in your project, try to debug it. It's part of development as well. :)
If you are in hurry to fix this issue in this weekend try to use the following code snippet.
// collect data from bundle
let constFileURL = Bundle.main.url(forResource: "AppConst", withExtension: "json")!
let data = try! Data(contentsOf: constFileURL)
// try to write data in document directory
do {
let constFileURL = try saveFileInDocumentDirectory(filePath: "MyFolder/AppConst.json", data: data)
// use your `constFileURL`
} catch (let error as FileOperationError) {
switch error {
case .fileAlreadyExists(let url):
let data = try! Data(contentsOf: url)
print(String(data: data, encoding: .utf8))
case .IOError(let error):
print("IO Error \(error)")
}
} catch {
print("Unknown Error \(error)")
}
// Helpers
enum FileOperationError: Error {
case fileAlreadyExists(url: URL)
case IOError(Error)
}
func saveFileInDocumentDirectory(filePath: String, data: Data) throws -> URL {
// final destination path
let destURLPath = fullURLPathOf(filePath, relativeTo: .documentDirectory)
// check for file's existance and throw error if found
guard FileManager.default.fileExists(atPath: destURLPath.path) == false else {
throw FileOperationError.fileAlreadyExists(url: destURLPath)
}
// Create Intermidiate Folders
let intermidiateDicPath = destURLPath.deletingLastPathComponent()
if FileManager.default.fileExists(atPath: intermidiateDicPath.path) == false {
do {
try FileManager.default.createDirectory(at: intermidiateDicPath, withIntermediateDirectories: true, attributes: nil)
} catch {
throw FileOperationError.IOError(error)
}
}
// File Writing
do {
try data.write(to: destURLPath, options: .atomic)
} catch {
throw FileOperationError.IOError(error)
}
return destURLPath
}
func fullURLPathOf(_ relativePath: String, relativeTo dic:FileManager.SearchPathDirectory ) -> URL {
return FileManager.default.urls(for: dic, in: .userDomainMask).first!.appendingPathComponent(relativePath)
}
Original Answer
Why don't you just return "MyFolder/\(fileName)" on successful file operation? If you need to access the path later you can always do that using FileManager APIs.
let docDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let constFilePath = docDir.appendingPathComponent("MyFolder/\(fileName)")
// Access const file data
do {
let fileData = try Data(contentsOf: constFilePath)
// Use you data for any further checking
} catch {
// Error in reading file data
print("Error in file data access : \(error)")
}
let filename = getDocumentsDirectory().appendingPathComponent(upload.fileName)
print("deleting")
let fileNameToDelete = upload.fileName
var filePath = ""
// Fine documents directory on device
let dirs : [String] = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.allDomainsMask, true)
if dirs.count > 0 {
let dir = dirs[0] //documents directory
filePath = dir.appendingFormat("/" + fileNameToDelete)
print("Local path = \(filePath)")
} else {
print("Could not find local directory to store file")
return
}
print(filename)
print("deleting111111")
do {
let fileManager = FileManager.default
// Check if file exists
print("filePath")
print(filePath)
print("filePath")
if fileManager.fileExists(atPath: filePath) {
// Delete file
try fileManager.removeItem(atPath: filePath)
} else {
print("File does not exist")
}
}
catch let error as NSError {
print("An error took place: \(error)")
}}
This gets printed below. Why is delete not working? Why is above function throwing to me on file exists it does not exist
deleting
Local path = /var/mobile/Containers/Data/Application/C763B3ED-3371-47AB-8F61-4F086D01E430/Documents/profile-FFCEBEA9-2F8D-49E2-9A09-2BF87BD0B542--A9636AF4-350D-4D72-A4BD-E4F2B183F4BB.png
file:///var/mobile/Containers/Data/Application/C763B3ED-3371-47AB-8F61-4F086D01E430/Documents/profile-FFCEBEA9-2F8D-49E2-9A09-2BF87BD0B542--A9636AF4-350D-4D72-A4BD-E4F2B183F4BB.png
deleting111111
filePath
/var/mobile/Containers/Data/Application/C763B3ED-3371-47AB-8F61-4F086D01E430/Documents/profile-FFCEBEA9-2F8D-49E2-9A09-2BF87BD0B542--A9636AF4-350D-4D72-A4BD-E4F2B183F4BB.png
filePath
File does not exist
If you created the file by writing to a filename generated with
filename = getDocumentsDirectory().appendingPathComponent(nameOfImage+".PNG")
...then it is absolutely crucial that, when the time comes to delete the file, you generate the filename in exactly the same way. That is not what you are doing.
Indeed, in the code you have shown, you do generate a variable called filename with code that looks similar:
let filename = getDocumentsDirectory().appendingPathComponent(upload.fileName)
...but then you never use filename for anything! Thus you keep faking yourself out. You create filename, you print filename, but you do not use filename as the path to delete. You use some other variable, filePath, obtained in a different way.
put your remove code in DispatchQueue.main.async like this:
let filename = getDocumentsDirectory().appendingPathComponent(upload.fileName)
print("deleting")
let fileNameToDelete = upload.fileName
var filePath = ""
// Fine documents directory on device
let dirs : [String] = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.allDomainsMask, true)
if dirs.count > 0 {
let dir = dirs[0] //documents directory
filePath = dir.appendingFormat("/" + fileNameToDelete)
print("Local path = \(filePath)")
} else {
print("Could not find local directory to store file")
return
}
print(filename)
print("deleting111111")
do {
let fileManager = FileManager.default
// Check if file exists
print("filePath")
print(filePath)
print("filePath")
if fileManager.fileExists(atPath: filePath) {
// Delete file
DispatchQueue.main.async { // <- here
try fileManager.removeItem(atPath: filePath)
}
} else {
print("File does not exist")
}
}
catch let error as NSError {
print("An error took place: \(error)")
}}
Edit 2:
I tried Zip from marmelroy (https://github.com/marmelroy/Zip) and it failed on the zip-file created witch ZipArchive too.
Then I created another zip-file with Zip and this one worked fine for unzipping.
There seems to be a problem with ZipArchive for me. I will use Zip instead and thats it for me ...
Thx for the answers !!
End edit 2.
I need to unzip certain files so I manually installed SSZipArchive.
Copied all 3 folders (SSZipArchive, minizip, aes) to project, added #import "ZipArchive.h" in my bridging.h and all builds nicely.
Edit:
Used Carthage as recommended, but same behavior.
End edit.
I use Xcode 8 / Swift 3
After a few tests with no unzipping any file, I created my own zip using SSZipArchive.
let file = "file.txt"
let text = "some text" //just a text
let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
let path = dir?.appendingPathComponent(file)
//writing
do {
try text.write(to: path!, atomically: false, encoding: String.Encoding.utf8)
}
catch {
print("Failed writing")
}
Here I print() the Documentsdirectory: ["file.txt"]
let zipPath = tempZipPath() //tempZipPath is taken from the SSZipArchiveExample except it uses DocumentDirectory instead of Cache...
print("zipPath: \(zipPath)")
print zipPath: /var/mobile/Containers/Data/Application/EB439A61-07B9-4910-BF28-03E85C50B292/Documents/93428A1B-E5B6-434F-B049-632B7519B126.zip
let success = SSZipArchive.createZipFile(atPath: zipPath, withContentsOfDirectory: (dir?.absoluteString)!)
if success {
print("zipped")
} else {
print(" NOT zipped")
}
Again the Documentsdirectories entries: ["93428A1B-E5B6-434F-B049-632B7519B126.zip", "file.txt"]
// this is again taken from the original Example
guard let unzipPath = tempUnzipPath() else {
return
}
print("unzipPath: \(unzipPath)")
print unzipPath: /var/mobile/Containers/Data/Application/EB439A61-07B9-4910-BF28-03E85C50B292/Documents/E4FC7DE6-F21B-46D8-9953-DCBF86E2268E
Reading the entries of this new directory: []
let filePath = zipPath
var fileSize : UInt64 = 0
do {
let attr : NSDictionary? = try FileManager.default.attributesOfItem(atPath: filePath) as NSDictionary?
if let _attr = attr {
fileSize = _attr.fileSize();
print("fileSize: \(fileSize)")
}
} catch {
print("Error: \(error)")
}
this writes 22. I also tried to read the zip-file which works fine
From this part on I was desperate what to do
let url = URL(fileURLWithPath: zipPath)
print("url: \(url)")
print("url.path: \(url.path)")
print("url.absoluteString: \(url.absoluteString)")
print("url.absoluteURL: \(url.absoluteURL)")
print("url.absoluteURL.absoluteString: \(url.absoluteURL.absoluteString)")
The output of these lines look all good for me:
url:
file:///var/mobile/Containers/Data/Application/EB439A61-07B9-4910-BF28-03E85C50B292/Documents/93428A1B-E5B6-434F-B049-632B7519B126.zip
url.path:
/var/mobile/Containers/Data/Application/EB439A61-07B9-4910-BF28-03E85C50B292/Documents/93428A1B-E5B6-434F-B049-632B7519B126.zip
url.absoluteString:
file:///var/mobile/Containers/Data/Application/EB439A61-07B9-4910-BF28-03E85C50B292/Documents/93428A1B-E5B6-434F-B049-632B7519B126.zip
url.absoluteURL:
file:///var/mobile/Containers/Data/Application/EB439A61-07B9-4910-BF28-03E85C50B292/Documents/93428A1B-E5B6-434F-B049-632B7519B126.zip
url.absoluteURL.absoluteString:
file:///var/mobile/Containers/Data/Application/EB439A61-07B9-4910-BF28-03E85C50B292/Documents/93428A1B-E5B6-434F-B049-632B7519B126.zip
And then I tried them all (Strings of course. No url allowed)
var success2 = SSZipArchive.unzipFile(atPath: zipPath, toDestination: unzipPath)
if !success2 {
print("zipPath")
}
success2 = SSZipArchive.unzipFile(atPath: url.path, toDestination: unzipPath)
if !success2 {
print("url.path")
}
success2 = SSZipArchive.unzipFile(atPath: url.absoluteString, toDestination: unzipPath)
if !success2 {
print("url.absoluteString")
}
success2 = SSZipArchive.unzipFile(atPath: url.absoluteURL.path, toDestination: unzipPath)
if !success2 {
print("url.absoluteURL.path")
}
And it prints every line, so they all failed. At the end here comes the "long" way with unipFileAtPath. I tried this one too with zipPath, ...
All the same results.
do {
success2 = try SSZipArchive.unzipFileAtPath(url.path, toDestination: unzipPath, overwrite: true, password: nil, delegate: nil)
if !success2 {
return
}
} catch {
print("error \(error)")
print("error \(error.localizedDescription)")
}
error Error Domain=SSZipArchiveErrorDomain Code=-1 "failed to open zip
file" UserInfo={NSLocalizedDescription=failed to open zip file} error
failed to open zip file
Here the to func to get the path-strings. btw. I modified tempZipPath so returns an URL or an URL.path
but hey: I was desperate
func tempZipPath() -> String {
var path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
path += "/\(UUID().uuidString).zip"
return path
}
func tempUnzipPath() -> String? {
var path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
path += "/\(UUID().uuidString)"
let url = URL(fileURLWithPath: path)
do {
try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
} catch {
return nil
}
return url.path
}
Any suggestions? Is there something to consider manually installing SSZipArchive?
The tip "use .path instead of .absoluteString" didn't work for me as u can see above.
a desperate tartsigam
I had the problem, too. If you use url.path instead of url.absolutString, it works.
Thanks to vadian for the hint!
Before zip don't extract file and return false.
After put this in Build Settings / Apple CLang Prepocessing / Preprocessor Macros Debug e Release works here
GCC_PREPROCESSOR_DEFINITIONS: HAVE_INTTYPES_H HAVE_PKCRYPT HAVE_STDINT_H HAVE_WZAES HAVE_ZLIB MZ_ZIP_NO_SIGNING $(inherited)
In my case, using ObjC and manual installation, I forgot to Add the following GCC_PREPROCESSOR_DEFINITIONS:
$(inherited)
HAVE_INTTYPES_H
HAVE_PKCRYPT
HAVE_STDINT_H
HAVE_WZAES
HAVE_ZLIB
After adding these GCC_PREPROCESSOR_DEFINITIONS it worked as expected.
I am trying to save a file inside a subfolder in Documents directory but it won't save. I can't seem to find what's wrong. Here is what I've tried:
if let audioUrl = NSURL(string: "http://pillar.foundationu.com/wp-content/plugins/pillar-data-sync/php/htmlBreakdownResult.json") {
let documentsUrl = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
do {
let directoryContents = try NSFileManager.defaultManager().contentsOfDirectoryAtURL(documentsUrl, includingPropertiesForKeys: nil, options: NSDirectoryEnumerationOptions())
print(directoryContents[0].path!)
if NSFileManager().fileExistsAtPath(String(audioUrl.path!)) {
print("The file already exists at path")
} else {
// if the file doesn't exist
// just download the data from your url
if let myAudioDataFromUrl = NSData(contentsOfURL: audioUrl){
// after downloading your data you need to save it to your destination url
if myAudioDataFromUrl.writeToURL(NSURL(fileURLWithPath: directoryContents[0].path!), atomically: true) {
print("file saved")
} else {
print("error saving file")
}
}
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
Here is where I want to save the file:
/Users/rendell/Library/Developer/CoreSimulator/Devices/5A052CC5-FD34-44FD-B060-24D6F1970860/data/Containers/Data/Application/37753B0B-FAB0-478D-A7F8-98E3039D07DD/Documents/MyFolder2
But it keeps on giving me "error saving file".
First of all, use always writeToURL:options:error to get a (more) descriptive error message.
The issue is quite simple: You forgot to provide a file name.
Technically you're going to overwrite an existing folder with data. That's not possible.