I need to know the size of the file I am downloading (in bytes) into my app using NSURLConnection (GET). Here is my bytes recieved code below if it helps. What I need to know is how to get the filesize in bytes so that I can use it to show a UIProgressView.
- (void)connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)data
// A delegate method called by the NSURLConnection as data arrives. We just
// write the data to the file.
{
#pragma unused(theConnection)
NSInteger dataLength;
const uint8_t * dataBytes;
NSInteger bytesWritten;
NSInteger bytesWrittenSoFar;
assert(theConnection == self.connection);
dataLength = [data length];
dataBytes = [data bytes];
bytesWrittenSoFar = 0;
do {
bytesWritten = [self.fileStream write:&dataBytes[bytesWrittenSoFar] maxLength:dataLength - bytesWrittenSoFar];
assert(bytesWritten != 0);
if (bytesWritten == -1) {
[self _stopReceiveWithStatus:@"File write error"];
break;
} else {
bytesWrittenSoFar += bytesWritten;
} while (bytesWrittenSoFar != dataLength);
}
[data length] does return the size of the data in bytes which should give you the figure you're looking for.
But NSData provides -writeToFile:atomically: and -writeToFile:options:error methods for writing data to disk so I don't know why you're writing your own file I/O.
I believe the code is from the SimpleURLConnections sample code from Apple. Here is a direct quote from Apple's NSURLConnection method "connection:didReceiveData:"
"The delegate should concatenate the
contents of each data object delivered
to build up the complete data for a
URL load."
NSData does not provide methods to write incrementally to a file, it writes it all at once, so I think this method of writing incrementally to an NSOutputStream, here named fileStream, is correct. Using dataLength in this code will only return the length of the data that has been sent from the NSURLConnection to the delegate in a single call to "connection:didReceiveData:", not the overall length of the data for the entire connection. To achieve this I have added extra properties:
@property (nonatomic, assign) long long dataWrittenForWholeConnection;
@property (nonatomic, assign) long long dataLengthOfWholeConnection;
In connection:didReceiveResponse: I used the NSURLResponse method expectedContentLength to obtain the correct estimate of the receiving data's file size for the whole connection.
self.dataWrittenForWholeConnection = 0;
self.dataLengthOfWholeConnection = [httpResponse expectedContentLength];
In connection:didReceiveData: I changed the following:
// bytes written for current "connection:didReceiveData:" call
bytesWrittenSoFar += bytesWritten;
// cumulative data written for connection so far
self.dataWrittenForWholeConnection += bytesWritten;
// also update the progress bar
if (self.dataLengthOfWholeConnection != NSURLResponseUnknownLength) {
self.progressView.progress =
((float)self.dataWrittenForWholeConnection / (float)self.dataLengthOfWholeConnection);
}
By the end of the connection, this should be true:
assert(dataWrittenForWholeConnection == dataLengthOfWholeConnection);
Here is the printout from the same SimpleURLConnections code with my modifications to show what is really happening:
connection:didReceiveResponse:
Full size of data is 8789 bytes
connection:didReceiveData:
Data has 1408 bytes
Progress bar is at: 0.160200
1408 bytes of data were written, 1408 bytes so far
connection:didReceiveData:
Data has 1408 bytes
Progress bar is at: 0.320401
1408 bytes of data were written, 2816 bytes so far
connection:didReceiveData:
Data has 1408 bytes
Progress bar is at: 0.480601
1408 bytes of data were written, 4224 bytes so far
connection:didReceiveData:
Data has 1408 bytes
Progress bar is at: 0.640801
1408 bytes of data were written, 5632 bytes so far
connection:didReceiveData:
Data has 1408 bytes
Progress bar is at: 0.801001
1408 bytes of data were written, 7040 bytes so far
connection:didReceiveData:
Data has 1749 bytes
Progress bar is at: 1.000000
1749 bytes of data were written, 8789 bytes so far
connectionDidFinishLoading:
Connection Finished. 8789/8789 bytes of data were written.
Also, the NSOutputStream method "write:maxLength:" returns "the number of bytes actually written", so for some reason it may choose not to write all the data at once within a single call of "connection:didReceiveData:", thus the use of the do/while loop to check for this. But in this example it writes all the data in a single do/while iteration.
Also, you can use an NSFileHandle instead, and this will always write the whole data sent with each connection:didReceiveData: call, also it's incremental. Note however that writeData: is synchronous.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// write data to file handler
unsigned long long dataLength = [data length];
NSLog(@"Writing %qu bytes at %qu bytes", dataLength, [self.fileHandle offsetInFile]);
[self.fileHandle writeData:data];
NSLog(@"Wrote %qu bytes, now at %qu bytes", dataLength, [self.fileHandle offsetInFile]);
}