I am downloading PDF file in my application and I want to show download progress bar when PDF downloading get started,I searched for this but didn't get any useful tutorial or answer,Can any one tell me any link or tutorial? Please help me, Thanks in advance.
You can use an NSURLConnection (here's an example) to get the remote file. The NSURLConnectionDataDelegate gets called with progress as data is received.
In order to estimate progress, you need to know (or have an estimate of) the number of bytes in the downloading file.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// allocate mutable responseData when connection didReceiveResponse
[self.responseData appendData:data];
float progress = self.responseData.length / self.expectedLength;
// this is the percentage progress
}
You can follow this link to display PDF download progress even if with multiple PDF files are downloading in queue. I mean you can display download progress status not only for single PDF file but also for multiple downloading PDF files in the application:
http://allseeing-i.com/ASIHTTPRequest/How-to-use
You can use AFHTTPRequestOperation to get the remote file,
for the progress you can try this code :
This will show you progress in progress view.
[httpClient enqueueBatchOfHTTPRequestOperations:tempOperations
progressBlock:^(NSUInteger numberOfCompletedOperations, NSUInteger totalNumberOfOperations) {
float progressValue=(float)numberOfCompletedOperations/(float)totalNumberOfOperations;
NSLog(#"%f", progressValue);
NSLog(#"%d / %d", numberOfCompletedOperations, totalNumberOfOperations);
[progressValueLabel setText:[NSString stringWithFormat:#"%.0f%%", (progressValue * 100)]];
[progressView setProgress:progressValue];
}
completionBlock:nil];
Related
I am working on a browser application and i want to track how much internet data is consumed when a web page is load.
I am using this method to calculate data received by iOS application.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
NSLog(#"Did Receive Data %i", [resourceData length]);
length += [resourceData length];
NSLog(#"Data in MB: %.3f",(length/(1024*1024)));
}
I am opening http://www.google.com in iOS simulator and same browser application for android.
When page load completes iOS application show 20KB data used but android application shows 280KB data used.
In android there is a class to get data used in an active session, that class name is TrafficStats. I search a lot but in iOS, i don't find any class or method that have similar.
I also try this code when webViewDidFinishLoading called to get actual data consumed by webView to load that page.
-(void)webViewDidFinishLoad:(UIWebView *)webView{
NSString* script = #"document.documentElement.outerHTML.length";
length += [[webBrowser stringByEvaluatingJavaScriptFromString:script] floatValue];
NSLog(#"Data in MB: %.3f",(length/(1024*1024)));
//[self performSelectorOnMainThread:#selector(dataUsedByApp) withObject:nil waitUntilDone:5.0];
}
But this is also not accurate because it doesn't calculate the size of images and size is calculated with #"document.documentElement.outerHTML.length".
Please help me on this. :)
Use dataWithContentsOfURL method:
NSData *resultData = [NSData dataWithContentsOfURL:#"https://www.google.co.in/logos/doodles/2014/world-cup-2014-57-5105522332139520.2-hp.gif"];
NSUInteger dataLength = resultData.length;
But here to get the URL for the image, you require to parse the entire HTML which is getting in response by https://www.google.co.in.
Hope this is what you are looking for.
Update:
Get html page size :
NSData *resultData = [NSData
dataWithContentsOfURL:#"https://www.google.co.in"];
Parse all the resource are there in the page. For that you require to do XML parsing of the entire data response. (by converting in to NSString) For that you might require to look into internet.
Once you get all the resource URLs like I got for logo.gif, get its NSData length individualy
At the end summation of all these is your solution:
PageSize + AllResourcesSize = Entire Page Load Size
You can intercept the requests with the opensource WebViewProxy (https://github.com/marcuswestin/WebViewProxy).
I can imagine that this will work if you want to track all http traffic, or you can change the predicate to fit your case:
[WebViewProxy handleRequestsMatching:[NSPredicate predicateWithFormat:#"absoluteString MATCHES[cd] '^http:'"] handler:^(NSURLRequest* req, WVPResponse *res) {
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:req.URL] queue:queue completionHandler:^(NSURLResponse *netRes, NSData *data, NSError *netErr) {
//do your transfer logging here
....
[res respondWithData:data mimeType:netRes.MIMEType];
}];
}];
In my iphone app, I am displaying information of files added to documents directory, in a table view, as soon as those are added. For this I am using DirectoryWatcher class provided in one of the sample codes by apple.
Below is the block of code showing its use:
- (void)viewDidLoad
{
// start monitoring the document directory…
self.aDirectoryWatcher = [DirectoryWatcher watchFolderWithPath:[self applicationDocumentsDirectory] delegate:self];
// scan for existing documents
[self directoryDidChange:self.aDirectoryWatcher];
}
- (void)directoryDidChange:(DirectoryWatcher *)folderWatcher
{
[self reconcileData];
}
One of the information displayed in table view cell is- file size, which I am obtaining as below:
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:nil];
NSNumber * size = [fileAttributes objectForKey:NSFileSize];
Problem is-
When I am trying to add a large file, such as a movie file, then as
soon as transfer starts (copy or move operation) it invokes
directoryDidChange: immediately. It did not wait unless the transfer
is complete. So I always get size as 0.
In case of small sized files, such as images, it works fine.
Now I have two question:
Is there any way to know the complete size of file, which is in transfer state. eg. if message displayed is copying 30 MB of 100 MB, I want to get 100 MB?
Is there any alternative of DirectoryWatcher, which notifies only when file is completely added?
Please suggest.
You are currently looking to the file system, you should look into the response headers from your download requests.
For example when you use NSURLConnection to download the file, you can implement the delegate method connection:didReceiveResponse: and look into the response headers.
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSLog(#"Expected content length: %lld", httpResponse.expectedContentLength);
}
}
To get notified when it's finished you can implement connectionDidFinishLoading:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// Notify download success
}
and
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// Notify error
}
I am working on an iOS application that downloads images from amazon s3. I am trying to track progress of an image download.
I can not get the -(void)request:(AmazonServiceRequest *)request didSendData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite delegate method to fire.
This is the code that I have so far to set the delegate method.
-(void) viewDidLoad
{
self.s3 = [[AmazonS3Client alloc] initWithAccessKey:ACCESS_KEY_ID withSecretKey:SECRET_KEY];
self.s3.endpoint = [AmazonEndpoints s3Endpoint:US_WEST_2];
NSString *key = [[NSString alloc] initWithFormat:#"path1/%#", uniqueID];
S3GetObjectRequest *downloadRequest = [[S3GetObjectRequest alloc] initWithKey:key withBucket: PICTURE_BUCKET];
[downloadRequest setDelegate:self];
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText = #"Loading Picture...";
S3GetObjectResponse *downloadResponse = [s3 getObject:downloadRequest];
}
-(void)request:(AmazonServiceRequest *)request didSendData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
{
NSLog(#"Bytes Written: %i", bytesWritten);
NSLog(#"Total Bytes Written: %i", totalBytesWritten);
NSLog(#"Total Bytes Expected to Write: %i", totalBytesExpectedToWrite);
}
I managed to get this delegate method to work for uploading images, but can not seem to get it to work for downloading. What do I need to do differently to track download progress?
Thanks
I came across this while researching myself on AWS and thought I would post an answer. -(void)request:(AmazonServiceRequest *)request didSendData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
only works when sending data, as per the name.
If you have a ballpark on how big the file is (you could set up some sort of server request to get this information before starting the download, or if there is a typical amount). Then you could use -(void)request:(AmazonServiceRequest *)request didReceiveData:(NSData *)data and continue to append the data to an #property of NSMutableData by calling [self.data appendData:data], then measure self.data.length which returns the number of bytes to your meta data size estimate which you could convert to bytes.
Hope this helps!
AdamG is right.
-(void)request:(AmazonServiceRequest *)request didSendData:(long long)bytesWritten totalBytesWritten:(long long)totalBytesWritten totalBytesExpectedToWrite:(long long)totalBytesExpectedToWrite is only for upload.
When you want to keep track of the progress of a download, you should use:
-(void)request:(AmazonServiceRequest *)request didReceiveData:(NSData *)data
And here I wan't to add some colaboration of my own. If you want to know the size of the file to download, here's a good way to do it.
S3GetObjectMetadataRequest *getMetadataObjectRequest = [[S3GetObjectMetadataRequest alloc] initWithKey:YOUR_KEY withBucket:YOUR_BUCKET];
S3GetObjectMetadataResponse *metadataResponse = [[AmazonClientManager s3] getObjectMetadata:getMetadataObjectRequest];
NSString *filesizeHeader = metadataResponse.headers[#"Content-Length"];
fileSize = [filesizeHeader floatValue];
I've found the documentation to be a little bit silent about this.
Also, the AWS iOS Samples also don't contain a very good example. Actually, there's a comment stating that "The progress bar for downlaod is just an estimate. In order to accurately reflect the progress bar, you need to first retrieve the file size", but without clue about how to do it.
So, I've found this way by messing around with the getMetadataObjectRequest.debugDescription property.
Hope this helps!
I have single view for both uploading and downloading images and audio files. Here what I am doing
To start downloading i am using this :
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
and this trigger its delagate methods
connection:didReceiveResponse:
connection:didReceiveData:
connectionDidFinishLoading:
and in these methods I am calculating file size, showing downloading progress through progress bar and saving files in my device.
For uploading I am doing this
[NSURLConnection connectionWithRequest:request delegate:self];
and using this connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:
this delegate methods works fine upload file and also tells about bytesWritten, totalBytesWritten, totalBytesExpectedToWrite but it also calls
connection:didReceiveResponse:
connection:didReceiveData:
connectionDidFinishLoading:
and its valid because all are delegate methods.
But problem is I am using these three to handle downloading.
What is the correct way to work with NSURLConection regarding uploading and downloading data?
Reference Apple Doc
The best way for me would be to implement the delegates in dedicated classes (e.g DownloadingDelegate and UploadingDelegate) and instantiate different delegates for each connection. The download and upload process could then be handled totally independently.
Or, if the download and upload are not concurrent, it can be simpler to use a boolean as a flag and test it in your delegates functions.
For example, let say you use a boolean instance variable called downloading.
You will have for the download:
downloading = true;
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
And the upload:
downloading = false;
[NSURLConnection connectionWithRequest:request delegate:self];
Then in your delegate:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
if( downloading )
{
// handle the response when downloading ...
}
else
{
// when uploading
}
}
Save the pointers to the NSURLConnections and inside the delegates determine which one is used.
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
if(connection==downloaderConn) {
//handle download
}
else {
//handle upoad
}
}
You can Use AFNetworking for your task.
AFNetworking is a delightful networking library for iOS and Mac OS X. It's built on top of NSURLConnection, NSOperation, and other familiar Foundation technologies. It has a modular architecture with well-designed, feature-rich APIs that are a joy to use.
Find the SDK here
I'm quite new to Amazon S3 and I'm having difficulty downloading large files from S3.
I have successfully downloaded a file that is 35MB every time, but when the size of the file is really big around 500 MB - 1.7GB the application crashes.
When trying on the simulator I would get can't allocate region error after about 1GB of the download.
So then I tried it on the device. Now it seems to just crash at a random time and
no crash report is put in the device, therefor I'm having an issue debugging this problem.
At first I thought it was the device or even the simulator. But i'm not really sure.
Someone mentioned that S3 framework times out the downloads randomly occasionally for large files. Could this be the case?
I'm building the file by opening a data file seeking to the end, adding the data, then closing the file until the download is complete.
I'm not sure how to debug this problem.
Any help would be appreciated.
Thank you.
I am a maintainer of the AWS SDK for iOS. We recently patched the S3GetObjectResponse to allow the streaming of the data directly to disk without keeping the response data in memory.
S3GetObjectResponse.m
To enable this, you simply need to set the stream when creating your request:
NSOutputStream *outputStream = [[[NSOutputStream alloc] initToFileAtPath:FILE_NAME append:NO] autorelease];
[outputStream open];
S3GetObjectRequest *getObjectRequest = [[[S3GetObjectRequest alloc] initWithKey:FILE_NAME withBucket:BUCKET_NAME] autorelease];
getObjectRequest.outputStream = outputStream;
[s3 getObject:getObjectRequest];
Update: We added a post to our AWS Mobile Developer Blog on downloading large files with the AWS SDK for iOS that includes this info as well as other tips.
S3GetObjectRequest has NSMutableData* body where it appends all the data it downloads.
For large files as download progresses data is appended constantly, and it goes over the VM limit of 90MB and then app gets killed by iOS.
Quick and dirty workaround is to create your own S3GetObjectRequest and S3GetObjectResponse classes. AWS framework instantiates Response based on Class Name of Request (Class name of Request without last 7 chars "Request" and appends it with "Response", and tries to instantiate new class of that name).
Then to override -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data to release body all the time.
This is quick and dirty fix simply because you still have constant data allocation, appending and then release. But it works when you are in a pinch. For my usage of downloading files of 150-700mb, this simple hack kept memory usage of the app at 2.55mb average, +/- 0.2mb.
As stated by the author of ASIHTTP library, it is no longer maintained.
Request - LargeFileS3GetObjectRequest.h
#interface LargeFileS3GetObjectRequest : S3GetObjectRequest
#end
Request - LargeFileS3GetObjectRequest.m
#implementation LargeFileS3GetObjectRequest
#end
Response - LargeFileS3GetObjectResponse.h
#interface LargeFileS3GetObjectResponse : S3GetObjectResponse
#end
Response - LargeFileS3GetObjectResponse.m
#implementation LargeFileS3GetObjectResponse
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// allow original implementation to send data to delegates
[super connection:connection didReceiveData:data];
// release body and set it to NULL so that underlying implementation doesn't
// append on released object, but instead allocates new one
[body release];
body = NULL;
}
#end
Hope it helps.
You may want to stream the data to your application via ASIHTTPRequest
http://allseeing-i.com/ASIHTTPRequest/S3
NSString *secretAccessKey = #"my-secret-access-key";
NSString *accessKey = #"my-access-key";
NSString *bucket = #"my-bucket";
NSString *path = #"path/to/the/object";
ASIS3ObjectRequest *request = [ASIS3ObjectRequest requestWithBucket:bucket key:path];
[request setSecretAccessKey:secretAccessKey];
[request setAccessKey:accessKey];
[request startSynchronous];
if (![request error]) {
NSData *data = [request responseData];
} else {
NSLog(#"%#",[[request error] localizedDescription]);
}
/* Set up the Amazon client */
_s3 = [[AmazonS3Client alloc] initWithAccessKey:k_Amazon_ACCESS_KEY_ID withSecretKey:k_Amazon_SECRET_KEY];
_s3.endpoint = [AmazonEndpoints s3Endpoint:SA_EAST_1];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
/* Open a file stream for the download */
NSOutputStream *outputStream = [[NSOutputStream alloc] initToFileAtPath:[DOCUMENTS_DIRECTORY stringByAppendingPathComponent:k_Amazon_Video_Local_File_Name] append:NO];
[outputStream open];
/* Set up the s3 get object */
S3GetObjectRequest *getVideoRequest = [[S3GetObjectRequest alloc] initWithKey:k_Amazon_Video_Path withBucket:#""];
/* Set the stream */
getVideoRequest.outputStream = outputStream;
/* Get the response from Amazon */
S3GetObjectResponse *getObjectResponse = [_s3 getObject:getVideoRequest];
dispatch_async(dispatch_get_main_queue(), ^{
if(getObjectResponse.error != nil)
{
NSLog(#"S3 Error: %#", getObjectResponse.error);
}
else
{
NSLog(#"S3 - Video download complete and successful");
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:k_Amazon_Video_Downloaded];
}
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
});
});