In my app, I am using NSFileHandle to edit some file but it is not editing.
Below is the code: with comments & logs output
//Initialize file manager
NSFileManager *filemgr;
filemgr = [NSFileManager defaultManager];
//Initialize file handle
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
//Check if file is writable
if ([filemgr isWritableFileAtPath:filePath] == YES)
NSLog (#"File is writable");
else
NSLog (#"File is read only");
//Read 1st byte of file
NSData *decryptData = [fileHandle readDataOfLength:1];
//Print first byte & length
NSLog(#"data1: %d %#",[decryptData length],decryptData); //data2: 1 <37>
//Replace 1st byte
NSData *zeroData = 0;
[fileHandle writeData:zeroData];
//Read 1st byte to check
decryptData = [fileHandle readDataOfLength:1];
//Print first byte
NSLog(#"data2: %d %#",[decryptData length],decryptData); //data2: 1 <00>
NSURL *fileUrl=[NSURL fileURLWithPath:filePath];
NSLog(#"fileUrl:%#",fileUrl);
[fileHandle closeFile];
Any Suggestions?
If you want to write using NSFileHandle you need to open the file for writing as well as reading:
NSFileHandle *fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:filePath];
If you aren't sure if the file at the specified path is writable, you should check for the appropriate permissions before you open it and display an error to the user if they are insufficient for what you need to do.
Also, to write data you need to create an instance of NSData. The line of code
NSData *zeroData = 0;
is creating a nil object, not an object containing a zero byte. I think you want
int8_t zero = 0;
NSData *zeroData = [NSData dataWithBytes:&zero length:1];
Related
I'm currently working on an application that has to upload large files (mainly movies/videos) to the web. After reading what I can, I went the the approach of converting the movie to NSData and then including that as the NSURLConnection's HTTPBody. However, upon converting the movie (which was originally an ALAsset) into NSData, I receive a memory warning and then a subsequent crash.
I have no idea how I would go about uploading these types of large files, if that data just causes an instant crash. One solution that I was thinking of is writing to the filesystem and then uploading a file directly from there, but I have not been able to find any information on how one would accomplish this.
Here is the relevant code that I use. If there is something that I'm doing wrong right here, I'd love to know.
ALAssetRepresentation *representation = [asset defaultRepresentation];
Byte *buffer = (Byte *)malloc([representation size]);
NSUInteger buffered = [representation getBytes:buffer fromOffset:0.0 length:[representation size] error:nil];
uploadData = [NSData dataWithBytes:buffer length:buffered];
free(buffer);
Assuming that it makes sense to upload the movie in its native format, you can really make this easier using the BSD (ie Unix) section 3 interface:
given a filePath, open the file and get an int file descriptor (fd)
with fd, get the length of the file
keep track of how much you've loaded so you know where to get more data
use mmap(3) to map in JUST the data you want to upload at any time, and use the void * pointer returned by mmap as the location of the data
when the data has been sent, munmap the old data chunk and mmap a new chunk
after all data is sent, munmap the last chunk, the close(fd).
No temporary memory - no mallocs. I use mmap whenever I have to deal with huge files.
Edit: you can also use NSData dataWithContentsOfFile:options with options set to use mmap. You would then use the byte pointer to read small chunks as you need them.
In case anyone got here and couldn't solve your problems, I figured out a way to do this.
You have to firstly write your ALAssetRepresentation to disk (as described here):
NSUInteger chunkSize = 100 * 1024;
NSString *tempFile = [NSTemporaryDirectory() stringByAppendingPathComponent:#"temp.tmp"];
uint8_t *chunkBuffer = malloc(chunkSize * sizeof(uint8_t));
NSUInteger length = [rep size];
NSFileHandle *fileHandle = [[NSFileHandle fileHandleForWritingAtPath: tempFile] retain];
if(fileHandle == nil) {
[[NSFileManager defaultManager] createFileAtPath:tempFile contents:nil attributes:nil];
fileHandle = [[NSFileHandle fileHandleForWritingAtPath:tempFile] retain];
}
NSUInteger offset = 0;
do {
NSUInteger bytesCopied = [rep getBytes:chunkBuffer fromOffset:offset length:chunkSize error:nil];
offset += bytesCopied;
NSData *data = [[NSData alloc] initWithBytes:chunkBuffer length:bytesCopied];
[fileHandle writeData:data];
[data release];
} while (offset < length);
[fileHandle closeFile];
[fileHandle release];
free(chunkBuffer);
chunkBuffer = NULL;
Then you have to create an NSData object that can map the disk without using memory resources (kind of like David's answer, but inspired by this answer):
NSError *error;
NSData *fileData = [NSData dataWithContentsOfFile:tempFile options:NSDataReadingMappedIfSafe error:&error];
if (!fileData) {
NSLog(#"Error %# %#", error, [error description]);
NSLog(#"%#", tempFile);
//do what you need with the error
}
EDIT Although, if you are uploading the file somewhere, you should open a connection and send small buffers of the file, kind of like what I did above. I had to write a C++ class to handle the socket and the connection
You probably shouldn't be trying to read the whole asset in one shot:
Byte *buffer = (Byte *)malloc([representation size]);
NSUInteger buffered = [representation getBytes:buffer fromOffset:0.0 length:[representation size] error:nil];
Instead, set up a loop and read from the asset in chunks. I've outlined the basic approach. You'll need to fill in a few gaps, but it should solve the memory issue.
You might also want to consider running this in a thread so you don't lock up the UI.
NSError error;
int bufferSize = 1000;
float offset=0.0;
//TODO: Open Connection
while (1)
{
Byte *buffer = (Byte *)malloc(bufferSize);
NSUInteger buffered = [representation getBytes:buffer fromOffset:offset length:bufferSize error:&error];
//TODO: Write data
//TODO: Increment offset, check errors
free(buffer);
//if (done){
//break;
//}
}
//TODO close eonnection
I found this snippet online to write, and then append data to a text file:
- (void)appendText:(NSString *)text toFile:(NSString *)filePath {
// NSFileHandle won't create the file for us, so we need to check to make sure it exists
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:filePath]) {
// the file doesn't exist yet, so we can just write out the text using the
// NSString convenience method
NSError *error = noErr;
BOOL success = [text writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
if (!success) {
// handle the error
NSLog(#"%#", error);
}
}
else {
// the file already exists, so we should append the text to the end
// get a handle to the file
NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:filePath];
// move to the end of the file
[fileHandle seekToEndOfFile];
// convert the string to an NSData object
NSData *textData = [text dataUsingEncoding:NSUTF8StringEncoding];
// write the data to the end of the file
[fileHandle writeData:textData];
// clean up
[fileHandle closeFile];
}
}
This makes sense to me. I have a class that has 3 properties, of NSString, NSInteger, and NSString. When I try to use this method, I do this:
for (MyObject *ref in array) {
NSString *stringToFile = [NSString stringWithFormat:#"%#\t%i\t%#", ref.ChrID, ref.Position, ref.Sequence];
[self appendText:stringToFile toFile:filePath];
}
It doesn't look quite right. My data looks like this:
NSString *tab* NSInteger *single space* NSStringNSString *tab* NSInteger newline
NSStringNSString *tab* NSInteger newline
NSStringNSString *tab* NSInteger newline
NSStringNSString *tab* NSInteger newline
NSStringNSString *tab* NSInteger newline
NSStringNSString *tab* NSInteger newline
NSStringNSString *tab* NSInteger newline
NSStringNSString *tab* NSInteger newline
...
I'm not sure what is going on to make it look like this. When I NSLog the data, it looks fine. But something with the first line gets messed up, and then everything seems to be off. Any thoughts? Thanks.
There are several issues with the method appendText:
if the file does not exist, the first line is written with the NSString writeToFile method without the \n
following lines are written with the NSData writeData method
it's very inefficient to use a filemanager to check for existence, get a filehandle, seek to EOF and then write just one line, omitting the close too. And repeating this for every following line.
So better do it this way:
get the filehandle for writing, it will be created if it's not there yet
seek to EOF
do your loop with writeData for each line
close the file
I want to write data to a .txt file without replacing its older contents.I have moved the file from the NSMainBundle to the documents directory.I am able to write the the file by using the code
NSData *data=[NSKeyedArchiver archivedDataWithRootObject:recentMainArray];
NSFileHandle *myHandle = [NSFileHandle fileHandleForUpdatingAtPath:filePath];
[myHandle seekToEndOfFile];
[myHandle writeData:data];
[myHandle closeFile];
But when i try to display the contents of the file,i don't have any data in that.File exists in that path also.This is the following code i use to display the contents.
NSFileManager *manager = [NSFileManager defaultManager];
//filePath - > the documents directory file path
if([manager fileExistsAtPath:filePath]) {
NSMutableArray *savedArray = [[NSMutableArray alloc] initWithContentsOfFile:filePath];
NSMutableArray *storedRecentID = [savedArray objectAtIndex:0];
NSMutableArray *storedRecentName = [savedArray objectAtIndex:1];
NSLog(#"ID:%#",[savedArray objectAtIndex:0]);
NSLog(#"Name:%#",[savedArray objectAtIndex:1]);
}
else {
NSLog(#"file not found, save something to create the file");
}
"null" which is printed as result for those two nslogs.
Please anybody let me know a solution for this problem.FYI,i am using a simulator to test,is this creating a problem.Please suggest me a solution.I have searched a lot and i am not able to find a solution.
Thank you all in advance
Read the data into some Mutable structure (dictionary, array, string) and then append your new data into the same satring. Now write the data into the same path. So the new appended data will be written in file.
I want to grab all URL's accessed in webview and write them to a text file.I don't want to use writeToFile coz in -(BOOL)webView method it would overwrite instead of appending to file. I can create file but all it writes to that file is string I used to create the file using FileManager createFileAtPath with Content ...
Also in the -(BOOL) webView method... when I tried to see if the file is read-only or writable ( isWritableFileAtPath ) it gives read-only.
POSIX Permissions in viewDidLoad -> 511 ...
checked file attributes in terminal going to that location its -rwxrwxrwx
I am new to Stack Overflow don't know how to post code here so using pastebin...http://pastebin.com/Tx7CsXVB
- (void)viewDidLoad {
[super viewDidLoad];
[myBrowser loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.apple.com"]]]; // UIWebView *myBrowser; is an instance variable
NSFileManager *fileMgr=[NSFileManager defaultManager];
NSDictionary* fileAttrs = [NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:777] forKey:NSFilePosixPermissions]; /*for setting attribute to rwx for all users */
NSDictionary *attribs; // To read attributes after writing something to file
NSString *aPath = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/file1.txt"];
myBrowser.delegate = self; // UIWebView delegate Self
// if the File Doesn't exist create one
if([fileMgr fileExistsAtPath:aPath])
{
NSLog(#"File Exists at this Location");
}
else
{
NSString *someString = #"This is start of file";
NSData *startString =[someString dataUsingEncoding: NSASCIIStringEncoding];
[fileMgr createFileAtPath:aPath contents:startString attributes: fileAttrs]; // earlier attributes was nil changed to fileAttrs
}
NSLog(#"aPath is %#",aPath);
attribs = [fileMgr attributesOfItemAtPath:aPath error: NULL];
NSLog (#"Created on %#", [attribs objectForKey: NSFileCreationDate]);
NSLog (#"File type %#", [attribs objectForKey: NSFileType]);
NSLog (#"POSIX Permissions %#", [attribs objectForKey: NSFilePosixPermissions]);
}
//UIWebView delegate calls this method every time user touches any embedded URL's in the current WebPage. I want to grab all the URL's accessed and write them to file.
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSFileManager *fileManager =[NSFileManager defaultmanager];
NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/file1.txt"];
fileHandle = [NSFileHandle fileHandleForWritingAtPath:path];
[fileHandle seekToEndOfFile]; // Moved up next to fileHandleForWritingAtPath since the above would place pointer to start of file again so setting handle to seek to End of File
/* section of code to check if the file at that path is writable or not */
if ([fileManager isWritableFileAtPath:path] == YES)
NSLog (#"File is writable");
else
NSLog (#"File is read only");
/* section of code to check if the file at that path is writable or not ENDS*/
NSURL *url = request.URL;
NSString *currenturl = url.absoluteString;
NSString *currentURL = [NSString stringWithFormat:#"%#\n",currenturl];
NSString *str =[NSString stringWithFormat:#"%#",currentURL];/* has already been set up */
[fileHandle writeData:[str dataUsingEncoding:NSUTF8StringEncoding]];
// testing if string has been written to file by reading it...
NSData *dataBuffer = [fileMgr contentsAtPath:path];
NSString *some;
some = [[NSString alloc] initWithData:dataBuffer encoding:NSASCIIStringEncoding];
NSLog(#"SOme String is: %#",some);
[fileHandle closeFile];
}
I think your issue may be that you're testing Documents/file1.txt and not /Documents/file1.txt
The leading '/' is important
[edit]
May I make a suggestion? Distill this down to what works first and then figure out what makes it fail?
I would recommend using the following form and continuing from there:
if ([fileManager isWritableFileAtPath: #"/Documents/file1.txt"] == YES)
NSLog (#"File is writable");
else
NSLog (#"File is read only");
[/edit]
I have a score system and I would like to log all scores in a text file separated by line breaks. Here is my current save code:
NSData *dataToWrite = [[NSString stringWithFormat:#"String to write ID:%i \n",random] dataUsingEncoding:NSUTF8StringEncoding];
NSString *docsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *path = [docsDirectory stringByAppendingPathComponent:#"text.txt"];
// Write the file
[dataToWrite writeToFile:path atomically:YES];
When retrieving this data, I only see the latest save. How do I make it so it saves all in a list?
Thanks.
[dataToWrite writeToFile:path atomically:YES]; overwrites the file at that location, replacing whatever is there with the contents of dataToWrite.
You can likely use NSFileHandle's fileHandleForWritingAtPath: and then call seekToEndOfFile to append to said file.
Do you have an example?
Try something like:
NSFileHandle *f = [NSFileHandle fileHandleForWritingAtPath: p];
[f seekToEndOfFile];
[f writeData: d];
[f close];
All typed into SO; the compiler/runtime might differ with my opinions of correctness.