large number of large files creating leak while creating zip file - iphone

NSData * buffer = [fileHandle readDataOfLength:chunkSize];
while ([buffer length] > 0)
{
[streamBIG writeData:buffer];
offset += [buffer length];
[fileHandle seekToFileOffset:offset];
buffer = [fileHandle readDataOfLength:chunkSize];
}
I use these particular process to zip more then 1 file of 25 - 30 MB but these increases memory i.e. live bytes continuously increase till all files are not written, because of which my app crashes

Try like this with Auto release pool
NSData * buffer = [fileHandle readDataOfLength:chunkSize];
while ([buffer length] > 0)
{
#autoreleasepool
{
[streamBIG writeData:buffer];
offset += [buffer length];
[fileHandle seekToFileOffset:offset];
buffer = [fileHandle readDataOfLength:chunkSize];
}
}
it will work...

Related

Compress Large UIImage

so what im doing is compressing an image repeatedly until it fits within a size frame. It it takes to many attempts, give up and later we tell the user the image is to large.
My issue is that after the image size has been reduced, its size goes WAY back up again when its changed to a UIImage.
Here is the code for reducing the image size:
double compressionRatio = 1;
int resizeAttempts = 5;
NSData * imgData = UIImageJPEGRepresentation([info objectForKey:#"UIImagePickerControllerOriginalImage"],compressionRatio);
NSLog(#"Starting Size: %i", [imgData length]);
//Trying to push it below around about 0.4 meg
while ([imgData length] > 400000 && resizeAttempts > 0) {
resizeAttempts -= 1;
NSLog(#"Image was bigger than 400000 Bytes. Resizing.");
NSLog(#"%i Attempts Remaining",resizeAttempts);
//Increase the compression amount
compressionRatio = compressionRatio*0.5;
//Test size before compression
NSLog(#"Current Size: %i",[imgData length]);
imgData = UIImageJPEGRepresentation([info objectForKey:#"UIImagePickerControllerOriginalImage"],compressionRatio);
//Test size after compression
NSLog(#"New Size: %i",[imgData length]);
}
//Set image by comprssed version
savedImage = [UIImage imageWithData:imgData];
//Check how big the image is now its been compressed and put into the UIImageView
NSData *endData = UIImageJPEGRepresentation(savedImage,1.0);
NSLog(#"Ending Size: %i", [endData length]);
Ok now here is the console report:
Starting Image Size: 4076994
Image was bigger than 400000 Bytes. Resizing.
4 Attempts Remaining
Current Size: 4076994
New Compressed Size: 844482
Image was bigger than 400000 Bytes. Resizing.
3 Attempts Remaining
Current Size: 844482
New Compressed Size: 357459
Ending Image Size: 2090332
As you can see the image started at 4mb big. It was compressed to 350kb, then in the end it was made into a UIImage as 2mb.
Thanks in advance to anyone who can make any sense of this.
I have modified your function a bit and got result, try it, it reduce my 10MB image to 3MB.
-(void)compraseImage
{
UIImage *largeImage = [info objectForKey:#"UIImagePickerControllerOriginalImage"];
double compressionRatio = 1;
int resizeAttempts = 5;
NSData * imgData = UIImageJPEGRepresentation(largeImage,compressionRatio);
NSLog(#"Starting Size: %i", [imgData length]);
//Trying to push it below around about 0.4 meg
while ([imgData length] > 400000 && resizeAttempts > 0) {
resizeAttempts -= 1;
NSLog(#"Image was bigger than 400000 Bytes. Resizing.");
NSLog(#"%i Attempts Remaining",resizeAttempts);
//Increase the compression amount
compressionRatio = compressionRatio*0.5;
NSLog(#"compressionRatio %f",compressionRatio);
//Test size before compression
NSLog(#"Current Size: %i",[imgData length]);
imgData = UIImageJPEGRepresentation(largeImage,compressionRatio);
//Test size after compression
NSLog(#"New Size: %i",[imgData length]);
}
//Set image by comprssed version
savedImage = [UIImage imageWithData:imgData];
//Check how big the image is now its been compressed and put into the UIImageView
// *** I made Change here, you were again storing it with Highest Resolution ***
NSData *endData = UIImageJPEGRepresentation(largeImage,compressionRatio);
NSLog(#"Ending Size: %i", [endData length]);
NSString *path = [self createPath:#"myImage.jpg"];
NSLog(#"%#",path);
[endData writeToFile:path atomically:YES];
}
-(NSString *) createPath:(NSString *)withFileName
{
NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,
YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:withFileName];
return path;
}

ALAssets get video data

I am trying to access video data from ALAssets library using the below code
ALAssetRepresentation *rep = [asset defaultRepresentation];
Byte *buffer = (Byte*)malloc(rep.size);
NSError *error = nil;
NSUInteger buffered = [rep getBytes:buffer fromOffset:0.0 length:rep.size error:&error];
NSData *data = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];
It works fine for small video as well as pictures, But if am trying to get a large video, the code crashes saying
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[NSConcreteData initWithBytes:length:copy:freeWhenDone:bytesAreVM:]: absurd length: 4294967295, maximum size: 2147483648 bytes'
I don't have an idea what's going on. Any one any thoughts?
Thanks in advance!
I found the solution. I guess the crash may be due to huge memory spike when we upload large files, because I am buffering data. Now I read file data as 5 MB chunks and this fix the crash. I am pasting my code below.
- (NSData *)getDataPartAtOffset:(NSInteger)offset {
__block NSData *chunkData = nil;
if (fileAsset_){
static const NSUInteger BufferSize = PART_SIZE; // 5 MB chunk
ALAssetRepresentation *rep = [fileAsset_ defaultRepresentation];
uint8_t *buffer = calloc(BufferSize, sizeof(*buffer));
NSUInteger bytesRead = 0;
NSError *error = nil;
#try
{
bytesRead = [rep getBytes:buffer fromOffset:offset length:BufferSize error:&error];
chunkData = [NSData dataWithData:[NSData dataWithBytesNoCopy:buffer length:bytesRead freeWhenDone:NO]];
}
#catch (NSException *exception)
{
free(buffer);
chunkData = nil;
// Handle the exception here...
}
free(buffer);
} else {
NSLog(#"failed to retrive Asset");
}
return chunkData;
}
And I I'll call this function as
int offset = 0; // offset that keep tracks of chunk data
do {
#autoreleasepool {
NSData *chunkData = [self getDataPartAtOffset:offset];;
if (!chunkData || ![chunkData length]) { // finished reading data
break;
}
// do your stuff here
offset +=[chunkData length];
}
} while (1);
chilitechno's bit here worked for me.

Can't receive jpeg image from server

I am trying to receive a jpeg image from my c# server. The weird thing is when I run it with the debugger and have a break point anywhere in the method it works perfectly fine. Without a breakpoint then I get this error
Corrupt JPEG data: premature end of data segment
Here is my code
(void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
NSMutableData *data;
data = [NSMutableData new];
switch(eventCode) {
case NSStreamEventHasBytesAvailable:
{
uint8_t buffer[1024];
int len;
while([inputStream hasBytesAvailable]) {
len = [inputStream read:buffer maxLength:sizeof(buffer)];
if (len > 0)
{
[data appendBytes:(const void*)buffer length:sizeof(buffer)];
}
}
UIImage *images = [[UIImage alloc]initWithData:data];
[dvdCover setImage:images];
} break;
case NSStreamEventEndEncountered:
{
//UIImage *images = [[UIImage alloc]initWithData:data];
//[dvdCover setImage:images];
} break;
}
}
hi you can check this code hop it will help you...
case NSStreamEventHasBytesAvailable:
{
uint32_t max_size = 1000000; // Max size of the received imaged you can modify it as your reqirement.
NSMutableData* buffer = [[NSMutableData alloc] initWithLength: max_size];
NSInteger totalBytesRead = 0;
NSInteger bytesRead = [(NSInputStream *)stream read: [buffer mutableBytes] maxLength: max_size];
if (bytesRead != 0) {
while (bytesRead > 0 && totalBytesRead + bytesRead < max_size) {
totalBytesRead+= bytesRead;
bytesRead = [(NSInputStream *)stream read: [buffer mutableBytes] + totalBytesRead maxLength: max_size - totalBytesRead];
}
if (bytesRead >= 0) {
totalBytesRead += bytesRead;
}
else {
// read failure, report error and bail (not forgetting to release buffer)
}
[buffer setLength: totalBytesRead];
yourImageName.image = [UIImage imageWithData: buffer];
[buffer release];
} break;
It seems you are assuming the whole JPEG image will be transferred in one chunk, and you can just read it with one occurence of 'HasBytesAvailable' event. However you should also consider the case where the JPEG image is transferred to you in multiple chunks.
It might work for you if you set breakpoint, because your code execution might be halted somewhere, and your network buffer had plenty time to receive all bytes of the image. But without breakpoints it might not have time to do so.
Try refactoring your code to accumulate the bytes chunk, and only assume it's done when all bytes have been transferred. (Normally you have to know beforehand how many bytes the image is going to be -- or you can just capture the end of stream event)

iOS : Access JPEG metadata at Marker APP3

I have been using the Exif tags to store some data on jpeg files in the following manner:
CGImageSourceRef source = CGImageSourceCreateWithURL(baseURL, NULL);
NSDictionary *metadata = (NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source,0,NULL);
NSMutableDictionary *metadataAsMutable = [[metadata mutableCopy]autorelease];
NSMutableDictionary *EXIFDictionary = [[[metadataAsMutable objectForKey:(NSString *)kCGImagePropertyExifDictionary]mutableCopy]autorelease];
[EXIFDictionary setObject:[NSString stringWithFormat:#"%d",tag] forKey:(NSString *)kCGImagePropertyExifUserComment];
Now, I would like to use a custom Application Marker (APP3 at 0xFFE3) instead of Exif Marker.
(Refer - http://www.ozhiker.com/electronics/pjmt/jpeg_info/app_segments.html)
Could someone point me in the right direction.
PS: I am using a jailbroken iPad for this app.
Okay, it seems we have to go via file handler way for this. Here is what I did, though there may be a better way to do this.
Create a File handle:
NSString *filePath = currentImageObject.myFilePath;
NSFileHandle *fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:filePath];
if(!fileHandle){
return;
}
[fileHandle seekToEndOfFile];
unsigned long long eofOffset = [fileHandle offsetInFile];
Then iterate the file contents till you find a desired tag:
BOOL markerFound = NO;
BOOL dqtFound = NO;
while ((!markerFound) && (!dqtFound) && ([fileHandle offsetInFile] < eofOffset)) {
currentOffset += 1;
[fileHandle seekToFileOffset:currentOffset];
NSData *markerData = [fileHandle readDataOfLength:1];
currentOffset += 1;
NSInteger markerValue = (unsigned char)*(unsigned char *)[markerData bytes];
if (0xe0 == markerValue) {
currentOffset += 14;
[fileHandle seekToFileOffset:currentOffset];
NSData *xThumbnailData = [fileHandle readDataOfLength:1];
currentOffset += 1;
NSData *yThumbnailData = [fileHandle readDataOfLength:1];
currentOffset += 1;
NSInteger xThumbnail = (unsigned char)*(unsigned char *)[xThumbnailData bytes];
NSInteger yThumbnail = (unsigned char)*(unsigned char *)[yThumbnailData bytes];
NSInteger thumbnailSize = 3 * xThumbnail * yThumbnail;
currentOffset += thumbnailSize;
[fileHandle seekToFileOffset:currentOffset];
} else if (0xe3 == markerValue) {
markerFound = YES;
break;
} else if (0xdb == markerValue) {
dqtFound = YES;
break;
} else {
NSData *lengthData = [fileHandle readDataOfLength:2];
currentOffset += 2;
NSInteger length = (unsigned short)*(unsigned short *)[lengthData bytes];
length = NSSwapBigShortToHost(length);
length -= 2;
currentOffset += length;
[fileHandle seekToFileOffset:currentOffset];
}
}
This gives you the offset to the APP3 marker, incase you need to add your own app3 marker you can use a similar approach.

Base64 Encode File Using NSData Chunks

Update 4
Per Greg's suggestion I've created one pair of image/text that shows the output from a 37k image to base64 encoded, using 100k chunks. Since the file is only 37k it's safe to say the loop only iterated once, so nothing was appended. The other pair shows the output from the same 37k image to base64 encoded, using 10k chunks. Since the file is 37k the loop iterated four times, and data was definitely appended.
Doing a diff on the two files shows that on the 10kb chunk file there's a large difference that begins on line 214 and ends on line 640.
Small Image (37k) - 100k Chunks - Image output
Small Image (37k) - 100k Chunks - Base64 Text output
Small Image (37k) - 10k Chunks - Image output
Small Image (37k) - 10k Chunks - Base64 Text output
Update 3
Here's where my code is now. Cleaned up a bit but still producing the same effect:
// Read data in chunks from the original file
[originalFile seekToEndOfFile];
NSUInteger fileLength = [originalFile offsetInFile];
[originalFile seekToFileOffset:0];
NSUInteger chunkSize = 100 * 1024;
NSUInteger offset = 0;
while(offset < fileLength) {
NSData *chunk = [originalFile readDataOfLength:chunkSize];
offset += chunkSize;
// Convert the chunk to a base64 encoded string and back into NSData
NSString *base64EncodedChunkString = [chunk base64EncodedString];
NSData *base64EncodedChunk = [base64EncodedChunkString dataUsingEncoding:NSASCIIStringEncoding];
// Write the encoded chunk to our output file
[encodedFile writeData:base64EncodedChunk];
// Cleanup
base64EncodedChunkString = nil;
base64EncodedChunk = nil;
// Update progress bar
[self updateProgress:[NSNumber numberWithInt:offset] total:[NSNumber numberWithInt:fileLength]];
}
Update 2
So it looks like files that are larger than 100 KB get scrambled, but files under 100 KB are fine. It's obvious that something is off on my buffer/math/etc, but I'm lost on this one. Might be time to call it a day, but I'd love to go to sleep with this one resolved.
Here's an example:
Update 1
After doing some testing I have found that the same code will work fine for a small image, but will not work for a large image or video of any size. Definitely looks like a buffer issue, right?
Hey there, trying to base64 encode a large file by looping through and doing it one small chunk at a time. Everything seems to work but the files always end up corrupted. I was curious if anyone could point out where I might be going wrong here:
NSFileHandle *originalFile, *encodedFile;
self.localEncodedURL = [NSString stringWithFormat:#"%#-base64.xml", self.localURL];
// Open the original file for reading
originalFile = [NSFileHandle fileHandleForReadingAtPath:self.localURL];
if (originalFile == nil) {
[self performSelectorOnMainThread:#selector(updateStatus:) withObject:#"Encoding failed." waitUntilDone:NO];
return;
}
encodedFile = [NSFileHandle fileHandleForWritingAtPath:self.localEncodedURL];
if (encodedFile == nil) {
[self performSelectorOnMainThread:#selector(updateStatus:) withObject:#"Encoding failed." waitUntilDone:NO];
return;
}
// Read data in chunks from the original file
[originalFile seekToEndOfFile];
NSUInteger length = [originalFile offsetInFile];
[originalFile seekToFileOffset:0];
NSUInteger chunkSize = 100 * 1024;
NSUInteger offset = 0;
do {
NSUInteger thisChunkSize = length - offset > chunkSize ? chunkSize : length - offset;
NSData *chunk = [originalFile readDataOfLength:thisChunkSize];
offset += [chunk length];
NSString *base64EncodedChunkString = [chunk base64EncodedString];
NSData *base64EncodedChunk = [base64EncodedChunkString dataUsingEncoding:NSASCIIStringEncoding];
[encodedFile writeData:base64EncodedChunk];
base64EncodedChunkString = nil;
base64EncodedChunk = nil;
} while (offset < length);
I wish I could give credit to GregInYEG, because his original point about padding was the underlying issue. With base64, each chunk has to be a multiple of 3. So this resolved the issue:
chunkSize = 3600
Once I had that, the corruption went away. But then I ran into memory leak issues, so I added the autorelease pool apprach taken from this post: http://www.cocoadev.com/index.pl?ReadAFilePieceByPiece
Final code:
// Read data in chunks from the original file
[originalFile seekToEndOfFile];
NSUInteger fileLength = [originalFile offsetInFile];
[originalFile seekToFileOffset:0];
// For base64, each chunk *MUST* be a multiple of 3
NSUInteger chunkSize = 24000;
NSUInteger offset = 0;
NSAutoreleasePool *chunkPool = [[NSAutoreleasePool alloc] init];
while(offset < fileLength) {
// Read the next chunk from the input file
[originalFile seekToFileOffset:offset];
NSData *chunk = [originalFile readDataOfLength:chunkSize];
// Update our offset
offset += chunkSize;
// Base64 encode the input chunk
NSData *serializedChunk = [NSPropertyListSerialization dataFromPropertyList:chunk format:NSPropertyListXMLFormat_v1_0 errorDescription:NULL];
NSString *serializedString = [[NSString alloc] initWithData:serializedChunk encoding:NSASCIIStringEncoding];
NSRange r = [serializedString rangeOfString:#"<data>"];
serializedString = [serializedString substringFromIndex:r.location+7];
r = [serializedString rangeOfString:#"</data>"];
serializedString = [serializedString substringToIndex:r.location-1];
// Write the base64 encoded chunk to our output file
NSData *base64EncodedChunk = [serializedString dataUsingEncoding:NSASCIIStringEncoding];
[encodedFile truncateFileAtOffset:[encodedFile seekToEndOfFile]];
[encodedFile writeData:base64EncodedChunk];
// Cleanup
base64EncodedChunk = nil;
serializedChunk = nil;
serializedString = nil;
chunk = nil;
// Update the progress bar
[self updateProgress:[NSNumber numberWithInt:offset] total:[NSNumber numberWithInt:fileLength]];
// Drain and recreate the pool
[chunkPool release];
chunkPool = [[NSAutoreleasePool alloc] init];
}
[chunkPool release];
How are you converting back the base64 data to an image? Some implementations limit the maximum line length they will accept. Try inserting a line break every so many characters.