4

Hello I would like to run a thread and check the current downloaded size of a file.

This is what I use

UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://lasp.colorado.edu/home/wp-content/uploads/2011/03/suncombo1.jpg"]]];

NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];

NSString *jpegFilePath = [NSString stringWithFormat:@"%@/test.jpeg",docDir];
NSData *data2 = [NSData dataWithData:UIImageJPEGRepresentation(image, 1.0f)];//1.0f = 100% quality
[data2 writeToFile:jpegFilePath atomically:YES];
downloadStatus.text =[NSString stringWithFormat:@"size: %zd", malloc_size(data2)];

[image release];

I have also tried to change malloc_size(data2) into image but again it is not the real result. I know this does not have thread and do not check during the download process but what am I supposed to use here to see the file size?

Sarp Kaya
  • 3,686
  • 21
  • 64
  • 103
  • In short, use the `length` instance method of `NSData`. You're getting a different file size because it's not the same file having gone through `UIImage` and `UIImageJPEGRepresentation` conversion. Your suspicious looking results are, in fact, correct. See my updated answer. – Rob Dec 14 '12 at 04:49

3 Answers3

5

A couple of observations:

  1. Your question presumed that your attempts to retrieve the size of the NSData were failing. They are not. The correct way to get the size of a NSData is via length.

  2. Your confusion, though, stems from a faulty assumption that taking an externally generated JPEG on a roundtrip through UIImage and UIImageJPEGRepresentation would yield the identical NSData. This would have been extraordinarily unlikely. There are too many different JPG settings that could have changed (see the JPEG Wikipedia page). We certainly don't know what settings that original file used. I know that UIImage and/or UIImageJPEGRepresentation changed the color space of the file. I'd wager it's doing a lot of other things, too.

  3. So your results are correct. The original file was 2.6mb and the resulting file was 4.5mb. If you change the compressionQuality from 1.0 to 0.99, the resulting file is only 1.4mb! But if you want the original file, just save it first (like I do below).

Consider the following code which downloads the image file, saves it, loads it into a UIImage, re-extracts it via UIImageJPEGRepresentation, and saves another copy of the image:

// let's make filenames where we'll store the files

NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *suncomboOrig = [documentsPath stringByAppendingPathExtension:@"suncombo1-orig.jpg"];
NSString *suncomboReprocessed = [documentsPath stringByAppendingPathExtension:@"suncombo1-reprocessed.jpg"];

// let's download the original suncombo1.jpg and save it in Documents and display the size

NSURL *url = [NSURL URLWithString:@"http://lasp.colorado.edu/home/wp-content/uploads/2011/03/suncombo1.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
NSLog(@"original = %d", [data length]);
[data writeToFile:suncomboOrig atomically:NO];

// let's load that into a UIImage

UIImage *image = [UIImage imageWithData:data];

// let's extract data out of the image and write that to Documents, too, also logging the size of that

NSData *data2 = UIImageJPEGRepresentation(image, 1.0);
NSLog(@"reprocessed = %d", [data2 length]);
[data2 writeToFile:suncomboReprocessed atomically:NO];

What that does is it reports:

2012-12-13 22:30:39.576 imageapp[90647:c07] original = 2569128
2012-12-13 22:30:40.141 imageapp[90647:c07] reprocessed = 4382876

So the first file I saved (which I suspect is identical to what's on your server) was 2.5mb, and the file after doing a roundtrip to a UIImage and re-extracted via 4.3mb. If I look at the two files that the above code saved, I can confirm that these NSData sizes are correct.


My original answer was predicated on the presumption that the OP was either unable to retrieve the size of a NSData or that there was some subtle issue underlying the simple question (such as wanting to get the size before the download commenced). Anyway, I've expanded my answer above, but I'll keep my original answer for historical purposes:

Original Answer:

The NSData property length tells you how many bytes were downloaded. E.g. [data2 length].

If it's really big, you can use NSURLConnection to download it asynchronously, which, depending upon your web server, may provide total file size before the download commences in the method didReceiveResponse (with the expectedContentLength property in the NSHTTPURLResponse *response parameter).

The other nice thing about NSURLConnection downloading is that you don't have to load the entire file in memory as you're downloading it, but rather you can stream it directly to persistent storage, which is especially useful if you're downloading multiple large files at the same time. If you're downloading a reasonably sized file, using NSURLConnection to download is overkill, but it can be nice when downloading large files and you want a progress indicator (or want to get the file size before the download commences).

But if you just want to know how many bytes were downloaded to your NSData, use length.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • I have done this way. But still it does not give an accurate result. I picked a picture which sized 1.5 MB and it showed me something like 5xxxxxx bytes. However the previous one was 2.5 MB and it resulted as 4xxxxxx bytes. So I did not get any reliable result at all! – Sarp Kaya Dec 14 '12 at 02:55
  • @SarpKaya You have started from a faulty assumption that by putting an externally generated JPEG in a `NSData`, then loading it into a `UIImage`, and then extracting via `UIImageJPEGRepresentation` would give you the same file. Not likely if the JPEG was from external source. So `[data2 length]` is correct. If you want the original file, save it immediately after you first retrieve it and bypass the `UIImage` and the `UIImageJPEGRepresentation`. See code in my updated answer. – Rob Dec 14 '12 at 04:42
4

You can just use the C FILE class to get the file size.

 FILE * handle = fopen([jpegFilePath UTF8String], "r");
 fseek(handle, EOF); // seek to end of file
 int size = ftell(handle); // returns position in bytes
 fclose();
evanmcdonnal
  • 46,131
  • 16
  • 104
  • 115
  • 2nd and 4th line is giving error, 2nd expects 3, 4th expects one argument. – Sarp Kaya Dec 14 '12 at 03:00
  • I have changed it into FILE * handle = fopen([jpegFilePath UTF8String], "r"); fseek(handle, 0, SEEK_END); int size = ftell(handle); fseek(handle, 0, SEEK_SET); fclose(handle); downloadStatus.text =[NSString stringWithFormat:@"size: %zd", size]; Now it works as the previous comment but still, the expected outcome is not accurate... – Sarp Kaya Dec 14 '12 at 03:10
3

Because you say "current" size and mention a thread, I'm guessing you're trying to determine the file size as it is received. In that case, you can get the thread for free from an NSURLConnection, and you can get the data size from the delegate methods as it's received...

Create an instance variable for the downloaded data:

@property (nonatomic, strong) NSMutableData *data;

Create and launch a connection:

NSURL *url = [NSURL URLWithString:@"http://lasp.colorado.edu/home/wp-content/uploads/2011/03/suncombo1.jpg"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];

self.data = [NSMutableData data];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];

Implement the required methods in NSURLConnectionDataDelegate. For your question, the special part is this:

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [self.data appendData:data];

    NSUInteger bytesSoFar = [self.data length];
    // you're on the app main thread here, so you can do something
    // to the UI to indicate progress
    downloadStatus.text = [NSString stringWithFormat@"size: %d", bytesSoFar];
}

A good doc on the rest of the protocol is here. When the connection is complete, you can create the image as you did with the dataWithContentsOfURL...

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {

    UIImage *image = [[UIImage alloc] initWithData:self.data];
    downloadStatus.text = [NSString stringWithFormat@"size: %d", [self.data length]];
}
Venk
  • 5,949
  • 9
  • 41
  • 52
danh
  • 62,181
  • 10
  • 95
  • 136
  • Not that it matters, but as an aside, in iOS, `didReceiveData` (and `didReceiveResponse`) have been moved out of the `NSURLConnectionDelegate` (if you go to the iOS version of the `NSURLConnectionDelegate` [page](https://developer.apple.com/library/ios/#documentation/Foundation/Reference/NSURLConnectionDelegate_Protocol/Reference/Reference.html), they're confusingly marked as deprecated) and are now in the [`NSURLConnectionDataDelegate`](http://developer.apple.com/library/ios/#documentation/Foundation/Reference/NSURLConnectionDataDelegate_protocol/Reference/Reference.html). – Rob Dec 14 '12 at 01:30
  • I really wish Apple would clean up the documentation on that. (Sigh.) I've seen more than one "oh my gosh, it's deprecated!" post here on S.O. – Rob Dec 14 '12 at 01:32
  • Yeah. I'm happy there's aggressive competition from Google and maybe MSFT, too. This should be a golden age for developers and customers of all three. – danh Dec 14 '12 at 01:36
  • first one on the third line it says: Assigning to 'NSMutableData' from incompatible type 'id' – Sarp Kaya Dec 14 '12 at 04:00
  • Oh, I missed the asterisk on the declaration. Will add that. (first line) – danh Dec 14 '12 at 06:04