writing files, objective zip - objective-c

I am using a library called objective zip to extract a file.
the relevant code:
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
ZipFile *unzipFile= [[ZipFile alloc] initWithFileName:requestFinishedPresentationPath mode:ZipFileModeUnzip];
NSArray *infos= [unzipFile listFileInZipInfos];
for (FileInZipInfo *info in infos) {
NSLog(#"- %# %# %d (%d)", info.name, info.date, info.size, info.level);
[unzipFile locateFileInZip:info.name];
// Expand the file in memory
ZipReadStream *read= [unzipFile readCurrentFileInZip];
NSMutableData *data = [[NSMutableData alloc] initWithLength:256];
int bytesRead = [read readDataWithBuffer:data];
[data writeToFile:[documentsDir stringByAppendingPathComponent:info.name] atomically:NO];
NSLog([documentsDir stringByAppendingPathComponent:info.name]);
[read finishedReading];
}
[unzipFile close];
the first nslog outputs the following:
- _some-path_/modules/reference-popup/ref-popup.js 2012-08-21 08:49:36 +0000 109 (-1)
the second nslog outputs the follows:
/Users/_USER_/Library/Application Support/iPhone Simulator/5.1/Applications/F2649DB7-7806-4C49-8DF9-BD939B2A9D5A/Documents/_some-path_/modules/slide-popup/slide-popup.css
Basically i think my read and data objects are not talking to each other... when I navigate to the right directory in my browser, all it does is show the two files, which are supposed to be directories (they are the most top-level directories) but the browser says they were written out as 1kb files. I try and view the file and its a 256 byte file... as per the buffer in the code.
What should I do instead?
To expand a zip (with all the files into a directory properly)?

Calling readDataWithBuffer will fill up the buffer (up to its length) and then pause the stream. You should actually put it in a loop and stop when bytesRead is 0. This is to allow expanding files bigger than available memory (think of a video, for example).
Also, consider that zip files have no directory structure: directories are simply embedded as part of the zipped file names. It's up to you to recreate the correct path. Usually you have some base directory, append the zipped file name and create the file with the resulting complete path.
In the Objective-Zip wiki, at the end, you can find a section titled Memory Management where a sample loop is present and commented. A more completed example is the following:
// Open zip descriptor
ZipFile *zip= [[ZipFile alloc] initWithFileName:zipPath mode:ZipFileModeUnzip];
NSMutableData *buffer= [[NSMutableData alloc] initWithLength:BUFFER_SIZE];
// Loop on file list
NSArray *zipContentList= [zip listFileInZipInfos];
for (FileInZipInfo *fileInZipInfo in zipContentList) {
// Check if it's a directory
if ([fileInZipInfo.name hasSuffix:#"/"]) {
NSString *dirPath= [documentsPath stringByAppendingPathComponent:fileInZipInfo.name];
[[NSFileManager defaultManager] createDirectoryAtPath:dirPath withIntermediateDirectories:YES attributes:nil error:NULL];
continue;
}
// Create file
NSString *filePath= [documentsPath stringByAppendingPathComponent:fileInZipInfo.name];
[[NSFileManager defaultManager] createFileAtPath:filePath contents:[NSData data] attributes:nil];
NSFileHandle *file= [NSFileHandle fileHandleForWritingAtPath:filePath];
// Seek file in zip
[zip locateFileInZip:fileInZipInfo.name];
ZipReadStream *readStream= [zip readCurrentFileInZip];
// Reset buffer
[buffer setLength:BUFFER_SIZE];
// Loop on read stream
int totalBytesRead= 0;
do {
int bytesRead= [readStream readDataWithBuffer:buffer];
if (bytesRead > 0) {
// Write data
[buffer setLength:bytesRead];
[file writeData:buffer];
totalBytesRead += bytesRead;
} else
break;
} while (YES);
// Close file
[file closeFile];
[readStream finishedReading];
}
// Close zip and release buffer
[buffer release];
[zip close];
[zip release];
Hope this helps.

Related

NSData writeToFile make synchronous or find out when it completes

I know there are a few questions asked on the behavior of NSData writeToFile atomically: and how that is supposed to be a SYNC method.
But in my experience it is not behaving like that. I've included pseudo code of my method that will accept and array of file names and will save them from Camera Roll to Documents folder of the app.
The For loop runs through each item in array and calls 'writeToFile'. My NSLogs show that the loop runs through and the NSLog message after the loop completion is displayed somewhere in the middle of the files being saved. for eg: I pass 4 images to this array to save, the NSLogs show that first file was saved and then it displays log that says it reached end of For loop and then the rest of the files being saved is displayed.
My question is - how do I make it synchronous or setup a completion handler of some sort that will only get called when the file write has completely finished. I need to eventually upload the files once they've been saved in the apps documents folder. Rest of the logic is in place. I just am having trouble finding the moment when saving of all files has completed.
I do not want to do a hack and set a delay because my file sizes and number of files can vary widely. Any guidance on this is much appreciated. Thanks
//'each item in 'arrayOfSelectedMedia' holds a dictionary of keys 'imageData' ,absolutePathToFileData' and 'filename'
-(void) saveArrayOfFiles: (NSMutableArray*) arrayOfSelectedMedia
toLocation: (NSString*) subFolder
whenCompletePostNotificationTo:(NSString*)backToCaller{
NSError *error;
if (![[NSFileManager defaultManager] fileExistsAtPath:globalChatsMediaFolder]){
[[NSFileManager defaultManager] createDirectoryAtPath:mediaFolder withIntermediateDirectories:NO attributes:nil error:&error]; //Create folder
}
else{
NSLog(#"folder %# exists already",mediaFolder);
}
NSString *completePath = [globalChatsMediaFolder stringByAppendingPathComponent:subFolder];
if (![[NSFileManager defaultManager] completePath]){
[[NSFileManager defaultManager] completePath withIntermediateDirectories:NO attributes:nil error:&error]; //Create folder
NSLog(#"creating folder %# ", completePath);
}
else{
NSLog(#"folder %# exists already", completePath);
}
/******* go through all files in array and save it to their specific folder ***********/
for(int i=0; i < [arrayOfSelectedMedia count]; i++){
NSString *saveFileAs = [NSString stringWithFormat:#"%#",[arrayOfSelectedMedia[i] objectForKey:#"filename"]];
NSURL *absolutePathToSelectedFile = [arrayOfSelectedMedia[i] objectForKey:#"absolutePathToFileInCameraRoll"];
// Create assets library
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init] ;
// Try to load asset at mediaURL
[library assetForURL:absolutePathToSelectedFile resultBlock:^(ALAsset *asset) {
// If asset exists
if (asset) {
// Type your code here for successful
ALAssetRepresentation *rep = [asset defaultRepresentation];
Byte *buffer = (Byte*)malloc((NSUInteger)rep.size);
NSUInteger buffered = [rep getBytes:buffer fromOffset:0.0 length:(NSUInteger)rep.size error:nil];
NSData *dataToUpload = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];
NSLog(#"%s , A direct BYTE size of file %d bytes", __PRETTY_FUNCTION__, [dataToUpload length]);
NSLog(#"absolute path to asset is %# ", asset.defaultRepresentation.url);
NSString *absolutePathToFile = [completePath stringByAppendingPathComponent:saveFileAs];
BOOL writeStatus = [dataToUpload writeToFile:absolutePathToFile options:NSDataWritingAtomic error:&error];
if(writeStatus){
NSLog(#"file %# with %lu bytes was successfully saved locally", saveFileAs, (unsigned long)[dataToUpload length]);
}
else{
NSLog(#"write to local file failed: %#",error);
}
}
else {
// Type your code here for not existing asset
NSLog(#"%s, File at path '%#' could not be found.",__PRETTY_FUNCTION__,absolutePathToSelectedFile);
}
} failureBlock:^(NSError *error) {
// Type your code here for failure (when user doesn't allow location in your app)
NSLog(#"%s, failure to read file at path: %#. Check if user has allowed access to file location.",__PRETTY_FUNCTION__,absolutePathToSelectedFile);
}];
} // end of For loop
NSLog(#"Finished saving array of Files to local Disk. back to caller provided as '%#'",backToCaller);
if(backToCaller.length > 0){ // i.e. it was not set to nil and I need to post notification
NSLog(#"posting notification to go back to Caller %#", backToCaller);
[[NSNotificationCenter defaultCenter] postNotificationName:backToCaller object:nil userInfo:nil];
}
}

Objective C - Create text file to read and write line by line in Cocoa

I building a Mac app,I have 2 problem:
I want to create a text file to read and write data on it. I don't know how to crate a text file to read and write data. Is it use
struct?
I want to create a XML file to read and write data on it. Can I create a struct for XML?
Do you have suggestion? Thanks in advance
Well, to create a file, just use
[[NSFileManager defaultManager] createFileAtPath:#"Your/Path" contents:nil attributes:nil];
This creates an empty file, which you can write to or read from. To write text (or XML), just use NSString's writeToFile:atomically:encoding:error: method like this
NSString *str = //Your text or XML
[str writeToFile:"Your/Path" atomically:YES encoding:NSUTF8StringEncoding error:nil];
To read from a file, just make an NSString with the contents of that file
NSString *contents = [NSString stringWithContentsOfFile:#"Your/Path"];
or, if it does not contain a string, get an NSData object from the file
NSData *contents = [NSData dataWithContentsOfFile:#"Your/Path"];
/**************************main.m******************************
NS FILE HANDLE READ & WRITE
reading and writing in same file
Created by abdulsathar on 6/16/14.
***************************************************************/
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{
#autoreleasepool //ARC
{
NSFileHandle *file;
//object for File Handle
NSError *error;
//crearing error object for string with file contents format
NSMutableData *writingdatatofile;
//create mutable object for ns data
NSString *filePath=[NSString stringWithFormat:#"/Users/chandrakumar/Documents/abdul/doc.txt"];
//telling about File Path for Reading for easy of access
file = [NSFileHandle fileHandleForReadingAtPath:#"/Users/chandrakumar/Documents/abdul/doc.txt"];
//assign file path directory
if (file == nil) //check file exist or not
NSLog(#"Failed to open file");
NSString *getfileContents = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];
//access file contents with out ns handle method
if (error) //check error flag for file present or not
NSLog(#"Error reading file: %#", error.localizedDescription);
NSLog(#"contents: %#", getfileContents);
//display file contents in main file
NSArray *listArray = [getfileContents componentsSeparatedByString:#"\n"];
//caluculate list of line present in files
NSLog(#"items = %ld", [listArray count]);
const char *writingchar = "how are you";
writingdatatofile = [NSMutableData dataWithBytes:writingchar length:strlen(writingchar)];
//convert string format into ns mutable data format
file = [NSFileHandle fileHandleForUpdatingAtPath: #"/Users/chandrakumar/Documents/abdul/new.txt"];
//set writing path to file
if (file == nil) //check file present or not in file
NSLog(#"Failed to open file");
[file seekToFileOffset: 6];
//object pointer initialy points the offset as 6 position in file
[file writeData: writingdatatofile];
//writing data to new file
[file closeFile];
//close the file
}
return 0;`enter code here`
}
/***********************************OUTPUT********************************************
2014-06-17 14:55:39.695 storage[4075:303] contents: hello how are you my dearservice
*************************************************************************************/

How to close a file in Objective-C

I´m trying to store some data in a file. I open the file the first iteration, and add the info. But, when the algorithm end, file size is 5kbs, it must be like 2,5 Mbs.
if (!isopen)
{
NSArray *paths;
paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
// Check DD
if ([paths count] > 0)
{
NSLog(#"Found a path");
fichero2 = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"out2.raw"];
NSLog(#"path %#",fichero2);
} else {
//d'oh, something went really wrong
NSLog(#"ERROR: could not open %s\n", "out.raw");
return -1;
}
isopen = 1;
}
else
{
// Write file
NSMutableData *data = [NSMutableData dataWithLength:1152*2];
[data appendBytes:((SInt16 *)[audioProcessor audioBuffer].mData) length:1152*2];
[data writeToFile:fichero2 atomically:YES];
}
Is really need to close the file? It auto close itself? So, why data is not stored?
I think you are creating a new NSData each loop,
Should be outside the dope of the iteration, no?
NSMutableData *data = [NSMutableData dataWithLength:1152*2];
then for
[data appendBytes:((SInt16 *)[audioProcessor audioBuffer].mData) length:1152*2];
[data writeToFile:fichero2 atomically:YES];

Appending to the end of a file with NSMutableString

I have a log file that I'm trying to append data to the end of. I have an NSMutableString textToWrite variable, and I am doing the following:
[textToWrite writeToFile:filepath atomically:YES
encoding: NSUnicodeStringEncoding error:&err];
However, when I do this all the text inside the file is replaced with the text in textToWrite. How can I instead append to the end of the file? (Or even better, how can I append to the end of the file on a new line?)
I guess you could do a couple of things:
NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:aPath];
[fileHandle seekToEndOfFile];
[fileHandle writeData:[textToWrite dataUsingEncoding:NSUTF8StringEncoding]];
[fileHandle closeFile];
Note that this will append NSData to your file -- NOT an NSString. Note that if you use NSFileHandle, you must make sure that the file exists before hand. fileHandleForWritingAtPath will return nil if no file exists at the path. See the NSFileHandle class reference.
Or you could do:
NSString *contents = [NSString stringWithContentsOfFile:filepath];
contents = [contents stringByAppendingString:textToWrite];
[contents writeToFile:filepath atomically:YES encoding: NSUnicodeStringEncoding error:&err];
I believe the first approach would be the most efficient, since the second approach involves reading the contents of the file into an NSString before writing the new contents to the file. But, if you do not want your file to contain NSData and prefer to keep it text, the second option will be more suitable for you.
[Update]
Since stringWithContentsOfFile is deprecated you can modify second approach:
NSError* error = nil;
NSString* contents = [NSString stringWithContentsOfFile:filepath
encoding:NSUTF8StringEncoding
error:&error];
if(error) { // If error object was instantiated, handle it.
NSLog(#"ERROR while loading from file: %#", error);
// …
}
[contents writeToFile:filepath atomically:YES
encoding:NSUnicodeStringEncoding
error:&err];
See question on stackoverflow
Initially I thought that using the FileHandler method in the accepted answer that I was going to get a bunch of hex data values written to my file, but I got readable text which is all I need. So based off the accepted answer, this is what I came up with:
-(void) writeToLogFile:(NSString*)content{
content = [NSString stringWithFormat:#"%#\n",content];
//get the documents directory:
NSString *documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
NSString *fileName = [documentsDirectory stringByAppendingPathComponent:#"hydraLog.txt"];
NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:fileName];
if (fileHandle){
[fileHandle seekToEndOfFile];
[fileHandle writeData:[content dataUsingEncoding:NSUTF8StringEncoding]];
[fileHandle closeFile];
}
else{
[content writeToFile:fileName
atomically:NO
encoding:NSUTF8StringEncoding
error:nil];
}
}
This way if the file doesn't yet exist, you create it. If it already exists then you only append to it. Also, if you go into the plist and add a key under the information property list UIFileSharingEnabled and set the value to true then the user can sync with their computer and see the log file through iTunes.
And here is a (slightly adopted) Swift version of Chase Roberts' solution:
static func writeToFile(content: String, fileName: String = "log.txt") {
let contentWithNewLine = content+"\n"
let filePath = NSHomeDirectory() + "/Documents/" + fileName
let fileHandle = NSFileHandle(forWritingAtPath: filePath)
if (fileHandle != nil) {
fileHandle?.seekToEndOfFile()
fileHandle?.writeData(contentWithNewLine.dataUsingEncoding(NSUTF8StringEncoding)!)
}
else {
do {
try contentWithNewLine.writeToFile(filePath, atomically: true, encoding: NSUTF8StringEncoding)
} catch {
print("Error while creating \(filePath)")
}
}
}

Easy way to get size of folder (ObjC/Cocoa)?

Right now I'm using this code to get the size of a folder:
NSArray *contents;
NSEnumerator *enumerator;
NSString *path;
contents = [[NSFileManager defaultManager] subpathsAtPath:folderPath];
enumerator = [contents objectEnumerator];
while (path = [enumerator nextObject]) {
NSDictionary *fattrib = [[NSFileManager defaultManager] fileAttributesAtPath:[folderPath stringByAppendingPathComponent:path] traverseLink:YES];
fileSize +=[fattrib fileSize];
}
[contents release];
[path release];
The problem is that its highly innacurate. It either adds a few megabytes or deducts a few megabytes from the actual size. For example I got the file size of an .app bundle and this method reported 16.2MB, whereas the actual thing is 15.8.
What's the best way to get the size of a folder?
Thanks
I needed to do this today myself, and I've found that the code in this post on the Cocoa-dev list is super fast and matches what Finder says to the byte. (don't forget to OR in the kFSCatInfoRsrcSizes flag so you get resource fork sizes, too!)
If you need more explanation on how to use it, just leave a comment and I'll edit this post. =)
The documentation for fileSize states it does not include the size of a resource fork. You may need to use the Carbon File Manager API to reliably calculate directory sizes.
I just wanted to second Dave DeLong's suggestion about the post on Cocoa-dev, but add a cautionary note to be sure to read all the posts in the thread. There is one by Rosyna that's particularly worth noting. In my case I followed that advice (changing max items per fetch to 40) and saw a speed jump as well as the end to a nasty crashing bug.
hope this will help
- (unsigned long long) fastFolderSizeAtFSRef:(NSString *)theFilePath
{
unsigned long long totalSize = 0;
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL isdirectory;
NSError *error;
if ([fileManager fileExistsAtPath:theFilePath])
{
NSMutableArray * directoryContents = [[fileManager contentsOfDirectoryAtPath:theFilePath error:&error] mutableCopy];
for (NSString *fileName in directoryContents)
{
if (([fileName rangeOfString:#".DS_Store"].location != NSNotFound) )
continue;
NSString *path = [theFilePath stringByAppendingPathComponent:fileName];
if([fileManager fileExistsAtPath:path isDirectory:&isdirectory] && isdirectory )
{
totalSize = totalSize + [self fastFolderSizeAtFSRef:path];
}
else
{
unsigned long long fileSize = [[fileManager attributesOfItemAtPath:path error:&error] fileSize];
totalSize = totalSize + fileSize;
}
}
}
return totalSize;
}
This is typically how it is done. 2 possibilities:
Check your byte -> megabyte conversion routines. Also, do you want megabytes or mebibytes? (It probably depends on what you're comparing it to.)
Try passing NO for the traverseLink parameter. There might very well be a symlink in the bundle pointing to something else that the routine you're comparing it to won't account for. You'll either count something in the bundle twice, or you'll include something outside the bundle entirely (most likely the former).
I know that this is an old topic. But for anyone out there looking for answers on how to do this,
[[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir];
if (isDir) {
NSPipe *pipe = [NSPipe pipe];
NSTask *t = [[[NSTask alloc] init] autorelease];
[t setLaunchPath:#"/usr/bin/du"];
[t setArguments:[NSArray arrayWithObjects:#"-k", #"-d", #"0", path, nil]];
[t setStandardOutput:pipe];
[t setStandardError:[NSPipe pipe]];
[t launch];
[t waitUntilExit];
NSString *sizeString = [[[NSString alloc] initWithData:[[pipe fileHandleForReading] availableData] encoding:NSASCIIStringEncoding] autorelease];
sizeString = [[sizeString componentsSeparatedByString:#" "] objectAtIndex:0];
bytes = [sizeString longLongValue]*1024;
}
else {
bytes = [[[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil] fileSize];
}
It will use terminal to determine a size for folders in bytes. And it will use Cocoa's built in NSFileManager to get the size of files. It's very fast, and gets the exact size that finder reports.
This code is as extension(category) to the NSFileManager class. It sums the sizes of all folder content.
Note that error treatment could be enhanced.
#interface NSFileManager(Util)
- (NSNumber *)sizeForFolderAtPath:(NSString *) source error:(NSError **)error;
#end
#implementation NSFileManager(Util)
- (NSNumber *)sizeForFolderAtPath:(NSString *) source error:(NSError **)error
{
NSArray * contents;
unsigned long long size = 0;
NSEnumerator * enumerator;
NSString * path;
BOOL isDirectory;
// Determine Paths to Add
if ([self fileExistsAtPath:source isDirectory:&isDirectory] && isDirectory)
{
contents = [self subpathsAtPath:source];
}
else
{
contents = [NSArray array];
}
// Add Size Of All Paths
enumerator = [contents objectEnumerator];
while (path = [enumerator nextObject])
{
NSDictionary * fattrs = [self attributesOfItemAtPath: [ source stringByAppendingPathComponent:path ] error:error];
size += [[fattrs objectForKey:NSFileSize] unsignedLongLongValue];
}
// Return Total Size in Bytes
return [ NSNumber numberWithUnsignedLongLong:size];
}
#end