Really stuck on trying to write code to unzip a file or directory on the iPhone.
Below is some sample code that I'm using to try and unzip a simple text file.
It unzips the file but its corrupt.
(void)loadView {
NSString *DOCUMENTS_FOLDER = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
NSString *path = [DOCUMENTS_FOLDER stringByAppendingPathComponent:#"sample.zip"];
NSString *unzipeddest = [DOCUMENTS_FOLDER stringByAppendingPathComponent:#"test.txt"];
gzFile file = gzopen([path UTF8String], "rb");
FILE *dest = fopen([unzipeddest UTF8String], "w");
unsigned char buffer[CHUNK];
int uncompressedLength = gzread(file, buffer, CHUNK);
if(fwrite(buffer, 1, uncompressedLength, dest) != uncompressedLength || ferror(dest)) {
NSLog(#"error writing data");
}
else{
}
fclose(dest);
gzclose(file);
}
I wanted an easy solution and didn't find one I liked here, so I modified a library to do what I wanted. You may find SSZipArchive useful. (It can also create zip files by the way.)
Usage:
NSString *path = #"path_to_your_zip_file";
NSString *destination = #"path_to_the_folder_where_you_want_it_unzipped";
[SSZipArchive unzipFileAtPath:path toDestination:destination];
Has "sample.zip" really been created with gZip? The .zip extension usually is used for archives created by WinZip. Those can also be decompressed using zLib, but you'd have to parse the header and use other routines.
To check, have a look at the first two bytes of the file. If it is 'PK', it's WinZip, if it's 0x1F8B, it's gZip.
Because this is iPhone specific, have a look at this iPhone SDK forum discussion where miniZip is mentioned. It seems this can handle WinZip files.
But if it's really a WinZip file, you should have a look at the WinZip specification and try to parse the file yourself. It basically should be parsing some header values, seeking the compressed stream position and using zLib routines to decompress it.
This code worked well for me for gzip:
the database was prepared like this:
gzip foo.db
the key was looping over the gzread(). The example above only reads the first CHUNK bytes.
#import <zlib.h>
#define CHUNK 16384
NSLog(#"testing unzip of database");
start = [NSDate date];
NSString *zippedDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"foo.db.gz"];
NSString *unzippedDBPath = [documentsDirectory stringByAppendingPathComponent:#"foo2.db"];
gzFile file = gzopen([zippedDBPath UTF8String], "rb");
FILE *dest = fopen([unzippedDBPath UTF8String], "w");
unsigned char buffer[CHUNK];
int uncompressedLength;
while (uncompressedLength = gzread(file, buffer, CHUNK) ) {
// got data out of our file
if(fwrite(buffer, 1, uncompressedLength, dest) != uncompressedLength || ferror(dest)) {
NSLog(#"error writing data");
}
}
fclose(dest);
gzclose(file);
NSLog(#"Finished unzipping database");
Incidentally, I can unzip 33MB into 130MB in 77 seconds or about 1.7 MB uncompressed/second.
This code will unzip any .zip file into your app document directory and get file from app resources.
self.fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSLog(#"document directory path:%#",paths);
self.documentDirectory = [paths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/abc", self.documentDirectory];
NSLog(#"file path is:%#",filePath);
NSString *fileContent = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"data.zip"];
NSData *unzipData = [NSData dataWithContentsOfFile:fileContent];
[self.fileManager createFileAtPath:filePath contents:unzipData attributes:nil];
// here we go, unzipping code
ZipArchive *zipArchive = [[ZipArchive alloc] init];
if ([zipArchive UnzipOpenFile:filePath])
{
if ([zipArchive UnzipFileTo:self.documentDirectory overWrite:NO])
{
NSLog(#"Archive unzip success");
[self.fileManager removeItemAtPath:filePath error:NULL];
}
else
{
NSLog(#"Failure to unzip archive");
}
}
else
{
NSLog(#"Failure to open archive");
}
[zipArchive release];
It's really hard to unzip any arbitrary zip file. It's a complex file format, and there are potentially many different compression routines that could have been used internally to the file. Info-ZIP has some freely licencable code to do it (http://www.info-zip.org/UnZip.html) that can be made to work on the iPhone with some hacking, but the API is frankly horrible - it involves passing command-line arguments to a fake 'main' that simulates the running of UnZIP (to be fair that's because their code was never designed to be used like this in the first place, the library functionality was bolted on afterwards).
If you have any control of where the files you're trying to unzip are coming from, I highly recommend using another compression system instead of ZIP. It's flexibility and ubiquity make it great for passing archives of files around in person-to-person, but it's very awkward to automate.
zlib isn't meant to open .zip files, but you are in luck: zlib's contrib directory includes minizip, which is able to use zlib to open .zip files.
It may not be bundled in the SDK, but you can probably use the bundled version of zlib use it. Grab a copy of the zlib source and look in contrib/minizip.
I haven't used the iPhone, but you may want to look at GZIP, which is a very portable open source zip library available for many platforms.
I had some luck testing this on the iPhone simulator:
NSArray *paths =
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *saveLocation =
[documentsDirectory stringByAppendingString:#"myfile.zip"];
NSFileManager* fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:saveLocation]) {
[fileManager removeItemAtPath:saveLocation error:nil];
}
NSURLRequest *theRequest =
[NSURLRequest requestWithURL:
[NSURL URLWithString:#"http://example.com/myfile.zip"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSData *received =
[NSURLConnection sendSynchronousRequest:theRequest
returningResponse:nil error:nil];
if ([received writeToFile:saveLocation atomically:TRUE]) {
NSString *cmd =
[NSString stringWithFormat:#"unzip \"%#\" -d\"%#\"",
saveLocation, documentsDirectory];
// Here comes the magic...
system([cmd UTF8String]);
}
It looks easier than fiddling about with zlib...
Related
I am able to compile the ENGLISH version which is already in sample for tesseract but not able to add other language like ara.traineddata.
I am doing like
Tesseract* tesseract = [[Tesseract alloc] initWithDataPath:#"tessdata" language:#"ara+eng"];
And it is recogninzing ENGLISH but for ara it is giving error
Error opening data file /Users/harshthakur/Library/Application Support/iPhone Simulator/7.0/Applications/3B0A1909-E1BA-45E9-99A0-FDEAB2CFF4E0/Documents/tessdata/ara.traineddata
Please make sure the TESSDATA_PREFIX environment variable is set to the parent directory of your "tessdata" directory.
Failed loading language 'ara'
any help will be greatly appreciated.
This is because the document folder does not have language file. Copy ara.traineddata file to your bundle and use this code to save your language file in document folder. Then try again. It will work fine.
- (void)storeLanguageFile {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *docsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *path = [docsDirectory stringByAppendingPathComponent:#"/tessdata/ara.traineddata"];
if(![fileManager fileExistsAtPath:path])
{
NSData *data = [NSData dataWithContentsOfFile:[[[NSBundle mainBundle] resourcePath] stringByAppendingString:#"/tessdata/ara.traineddata"]];
NSError *error;
[[NSFileManager defaultManager] createDirectoryAtPath:[docsDirectory stringByAppendingPathComponent:#"/tessdata"] withIntermediateDirectories:YES attributes:nil error:&error];
[data writeToFile:path atomically:YES];
}
}
Have a look here ,maybe it will be helpful to you .
Tesseract
here you can
-setLanguage:
- (BOOL)setLanguage:(NSString *)language
Override the language defined with -initWithDataPath:language:.
You probably need ara.cube.* files also.
Flipping the order of the languages could improve recognition rates, once you get it to run.
I have a bunch of images stored in an images directory within my Supported Files directory in Xcode. I want to be able to show one of those images. What is the best way to obtain a path to that image? Do I have to copy them to the Documents directory first? If so, how can I do that?
EDIT: I've tried the following to copy the image from Supporting Files to the Documents folder in the app. It successfully copies, but I can't get the image to show:
-(void)findImage:(NSString *)imageName
{
// First, test for existence.
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *appImagePath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.jpg",imageName]];
success = [fileManager fileExistsAtPath:appImagePath];
if (success)
{
return;
}
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultImagePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.jpg",imageName]];
success = [fileManager copyItemAtPath:defaultImagePath toPath:appImagePath error:&error];
if (!success)
{
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
self.imageDisplay.image = [UIImage imageNamed:appImagePath];
return;
}
This should do the trick:
[UIImage imageNamed:#"someImageName"];
EDIT:
Some additional information:
-imageNamed: will look through the entire main bundle of the application for an imagefile (preferrably an png) with the filename of "someImageName". You need not worry about its location or its extension, since it will be searched for in the mainbundle. Files that you import through the import-file-dialogue in xcode will be added to he main bundle.
This means:
If i have imported a file called myImage.png, calling [UIImage imageNamed:#"myImage"];from anywhere in my code will get me a UIImage-Object containing that image. Its amazingly simple, and maybe that startled you a bit ;)
Look it up in the docs if you like:
http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIImage_Class/Reference/Reference.html
A little embarrassing question, but I can find an answer which works in my case... I need to put some xml file (settings.xml) in order to read some data from it during application runtime.
According to some answers here and not only here, I have putted it here:
~/Library/Application Support/iPhone Simulator/5.0/[AppUUID]/Documents
and I'm trying to use it as follows:
// Loading data from external XML File
NSURL *url = [[NSBundle mainBundle]
URLForResource: #"settings" withExtension:#"xml"];
NSError *err;
if ([url checkResourceIsReachableAndReturnError:&err] == NO){
NSLog(#"FILE NOT FOUND");
}
Result: "FILE NOT FOUND".
I've tried to do put the file under any possible directory in
~/Library/Application Support/iPhone Simulator/5.0/[AppUUID]/ and efect is still the same.
I'm using XCode 4.2
If you are putting the file into the .../Documents folder then you need to use the following code to access it (you are looking for it in the App Bundle, which is a different location altogether):
NSString *docFolder = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filename = [docFolder stringByAppendingPathComponent:#"settings.xml"];
if ([[NSFileManager defaultManager] fileExistsAtPath:filename])
{
// Read file
}
else
{
NSLog(#"settings.xml file not found!");
}
In my game, I'm saving stats of the player in a plist that I store in the Documents directory. I have an empty dictionary of each stats that should be saved named "Default_Stats.plist" so that if it's the first time the app is loaded, it will copy it in the appropriate directory so it could be loaded and overwritten at will. The problem is, every time my app is loaded, it doesn't recognize the "Stats.plist" and overwrite it with the Default Stats, resetting every stats the player have made... And weird enough, it was perfectly working on the simulator, but not on the device. Here's my code :
In this method I read the stats :
- (void) readStatsFromFile{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *statsPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"Stats.plist"];
//Check if the file has already been created
if (![[NSFileManager defaultManager] fileExistsAtPath:statsPath]){
[self createStatsList];
}else{
stats = [[NSMutableDictionary dictionaryWithContentsOfFile:statsPath]retain];
}
}
Here's my creating method :
- (void) createStatsList{
NSString *statsPath = [[NSBundle mainBundle] bundlePath];
statsPath = [statsPath stringByAppendingPathComponent:#"Default_Stats.plist"];
stats = [[NSMutableDictionary dictionaryWithContentsOfFile:statsPath] retain];
[self writeStatsToFile];
}
And my writing method :
- (void) writeStatsToFile{
BOOL ok;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *statsPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"Stats.plist"];
ok = [stats writeToFile:statsPath atomically:YES];
if (!ok) {
NSLog(#"Couldn't write to file");
}else
NSLog(#"Stats written succesfully!");
}
Please help, I really don't understand what's wrong! I hope I've made myself clear enough!
Use filepath instead of absolute path.
Maybe duplicates exist in your mac, which makes exists=true on simulator, but not on device.
The easiest way to check would be to NSLog the paths encountered. Refer to these tools - they allow console logs to be captured for release builds running on your device.
Most likely that your documents directory just doesn't exist - on the simulator you share a documents directory with everyone on the Mac; on the device everyone has his own directory. Use the file manager method
createDirectoryAtURL:url withIntermediateDirectories:YES
to make sure that the directory is there before you try writing there. (I tend to use the URL methods instead of the file path methods).
PS. I'd recommend having one method that returns the path or url that you want. It's a good habit not to duplicate your code again and again.
I would do pretty much that, like everything in one session:
gets the URL for the file in the Document folder;
if the file is not there yet, copies the file from bundle to the Documents folder;
that should be the method for that, I have defined some macros for avoiding mistyping the file's name in the code:
- (NSURL *)statsFileURL {
#define NSStringFromFileNameWithExtension(filename, extension) [(filename) stringByAppendingPathExtension:(extension)]
#define kExtension #"plist"
#define kDefaultStatsFileName #"Default_Stats"
#define kCustomStatsFileName #"Stats"
NSURL *_returnURL = nil;
NSFileManager *_fileManager = [NSFileManager defaultManager];
NSURL *_documentDirectory = [[_fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *_myFileURLInDocumentFolder = [_documentDirectory URLByAppendingPathComponent:NSStringFromFileNameWithExtension(kDefaultStatsFileName, kExtension)];
if ([_fileManager fileExistsAtPath:[_myFileURLInDocumentFolder path]]) {
_returnURL = _myFileURLInDocumentFolder;
} else {
NSURL *_myFileURLInBundle = [[NSBundle mainBundle] URLForResource:kDefaultStatsFileName withExtension:kExtension];
if ([_fileManager fileExistsAtPath:[_myFileURLInBundle path]]) {
NSError *_error = nil;
if ([_fileManager copyItemAtURL:_myFileURLInBundle toURL:_myFileURLInDocumentFolder error:&_error]) {
if (_error == nil) {
_returnURL = _myFileURLInDocumentFolder;
} else {
// some error during copying
}
} else {
// some error during copying
}
} else {
// the file does not esixts at all, not even in the bundle
}
}
return _returnURL;
}
the URL always points inside the Documents folder, so you will have read/write access to the file – or will be nil if some error happens.
after you have the URL, you can restore back to file without any issue, and at some other point in runtime you can override the file for your convenience anytime.
NOTE: you may need to extend this code for a more detailed error handling, I put the comment only the places when you need to worry about potential errors.
I'm trying to copy a downloaded file to a specific folder in the app's documents directory but can't seem to get it working. The code I'm using is:
NSString *itemPathString = #"http://pathToFolder/folder/myFile.doc";
NSURL *myUrl = [NSURL URLWithString:itemPathString];
NSArray *paths = [fm URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
NSURL *folderPath = [[paths objectAtIndex:0] URLByAppendingPathComponent:#"folder"];
NSURL *itemURL = [documentsPath URLByAppendingPathComponent:#"myFile.doc"];
// copy to documents directory asynchronously
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSFileManager *theFM = [[NSFileManager alloc] init];
NSError *error;
[theFM copyItemAtURL:myUrl toURL:itemURL error:&error];
}
});
I can retrieve the file OK but can't copy it. Can anyone tell me if there's anything wrong with the above code?
If downloading a file from a server, if it's a reasonably small file (e.g. measured in kb, not mb), you can use dataWithContentsOfURL. You can use that method to load the file into memory, and then use the NSData instance method writeToFile to save the file.
But, if it's a larger file, you will want to use NSURLConnection, which doesn't try to hold the whole file in memory, but rather writes it to the file system when appropriate. The trick here, though, is if you want to download multiple files, you either have to download them sequentially, or encapsulate the NSURLConnection and the NSOutputStream such that you can have separate copies of those for each simultaneous download.
I have uploaded a project, Download Manager that demonstrates what a NSURLConnection implementation might look like, but it's non-trivial. You might rather want to contemplate using an established, third-party library, such as ASIHTTPRequest or RestKit.
If you want to access a folder with a given name you should check if it exists and if not create it. That could quite easy be done like this:
NSString *folder = [documentsPath stringByAppendingPathComponent:folderName];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error = nil;
if (![fileManager fileExistsAtPath:folder]) {
[fileManager createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:nil error:&error];
}
if (error != nil) {
NSLog(#"Some error: %#", error);
return;
}
EDIT
If you want to check if the folder was created properly on your device got to Organizer -> Devices -> [YourDevelopingDeviceWhereTheAppWasInstalled] -> Applications -> [YourApplication]
In the lower section you should at least see some folders like Documents. And if successful your created folders as well.
You need to create any intermediate directories prior to copying files. Check in the Simulator folder to see wether the "folder" directory is created in the applications Documents-folder.
Path to simulator is /Users/$username/Library/Application Support/iPhone Simulator/