HTTP response from dropbox zip download request (?dl=1) - ios

I'm downloading a zipped folder from dropbox, by adding the ?dl=1 code at the end of the shared link.
Everything works fine and dandy, except for the connection response content length field. When I check response.expectedContentLength value, it always comes up as -1
I've tried avoiding response gzip compression with
[httpRequest setValue:#"" forHTTPHeaderField:#"Accept-Encoding"];
I've tried checking in the didReceiveResponse method if expectedContentLength was -1 and doing a manual check just for headers using the absolute zip file link (contained in response.URL) with
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:response.URL
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:10];
theRequest.timeoutInterval = 5.0;
theRequest.HTTPMethod = #"HEAD";
NSHTTPURLResponse *res;
NSData * resData = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:&res error:nil];
long length = res.expectedContentLength;
"length" still comes up as -1.
The zip is generated by dropbox on the fly, so the initial request might be unable to get the content length. But in theory the response.URL value SHOULD be the final zipped file, and requesting it's headers should return a proper contentLength field, right?
Any ideas?

For anyone coming here later on:
I never got to find a way of getting it, short of fully downloading it (which kinda defeats the purpose), so that's that.
This is something you can't access (written on 16/06/16, do check again when you read). Dropbox doesn't set that particular header when downloading zipped folders.

Related

I'm getting header info for a file that doesn't exist?

I'm using code that I got from a tutorial for finding the "Date Modified" info of a file on a server. It goes as follows...
// create a HTTP request to get the file information from the web server
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:remoteFileURL];
[request setHTTPMethod:#"HEAD"];
NSHTTPURLResponse* response;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
// get the last modified info from the HTTP header
NSString* httpLastModified = nil;
if ([response respondsToSelector:#selector(allHeaderFields)]) {
httpLastModified = [[response allHeaderFields] objectForKey:#"Last-Modified"];
}
And, for the most part, it works! Wa-hoo.
Except, for a few files, it seems to be returning outdated information. In fact, for at least one, it's returning a date (Fri, 24 Apr 2015 04:32:55 GMT) for a file that doesn't even exist anymore. I deleted it from the server, but it's still returning that value every time I run my app, as if the file still existed.
I've checked and re-checked that the remoteFileURL is pointing to the right URL, and I've confirmed that the file doesn't exist on the server anymore. And, like I said, for most files it works perfectly, so obviously the system isn't entirely broken.
This is my first experience with NSMutableURLRequest and NSHTTPURLResponse and getting file headers and such. Is there something I don't know, that would explain this? Could there be a cache or something that needs to be cleared?
And ideally, what can I do to ensure that the proper info is being received by my app?
Thanks in advance!
Posting my comment as an answer
This type of thing happends because of caching mechanism of NSURL.
Try to add below line to remove all cached data.
[[NSURLCache sharedURLCache] removeAllCachedResponses];
If you want to ignore caching for particulate request try below code
NSURL *url = [NSURL URLWithString:aStrFinalURL];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];

iOS sending a POST and GET in the same request

I am successfully posting data as follows:
NSMutableURLRequest *scriptrequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"myurl.com"]];
[scriptrequest setHTTPMethod:#"POST"];
NSString *sendData =[NSString stringWithFormat:#"ID=%#&action=List", ID, nil];
NSData *scriptdata = [sendData dataUsingEncoding:NSUTF8StringEncoding];
[scriptrequest setHTTPBody:scriptdata];
NSError *scripterr;
NSURLResponse *scriptresponse;
NSData *scriptResponseData = [NSURLConnection sendSynchronousRequest:scriptrequest returningResponse:&scriptresponse error:&scripterr];
My question is, is it possible to also attach GET data to the same call?
GET and POST are different types of HTTP Request.
GET Request is majorly used for fetching the web content while POST is for insert/update some content.
So eventually A single HTTP request can only be of one of the following.
Http types include:
GET
HEAD
POST
PUT
DELETE
TRACE
OPTIONS
CONNECT
PATCH
More technical details at Wikipedia:
http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol
It looks to me like you are trying to retrieve data from your server, not update. A more typical way to form the request would be to GET http://myurl.com?ID=123&action=List. However, this really depends on how the server code is written.

Upload image from iOS to Django

I need help getting a basic understanding of uploading a file to a Django view via a post request. This is the Django form I'd like to upload the image to:
https://domfa.de/upload_profile/
This is the views.py code for this exact Django view URL:
def profile_picture(request):
if request.POST:
form = UserProfileForm(request.POST, request.FILES)
obj = form.save(commit=False)
obj.user_id = request.user.id
obj.profile_picture = obj.profile_picture
obj.save()
return render_to_response('profile.html', args, RequestContext(request))
else:
formNew = UserProfileForm()
args = {}
args.update(csrf(request))
args['uid'] = request.user.id
args['form'] = formNew
return render_to_response('profile.html', args, RequestContext(request))
And the actual form for this is extremely simple with just a single field for the actual profile picture:
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('profile_picture',)
def __init__(self, *args, **kwargs):
super(UserProfileForm, self).__init__(*args, **kwargs)
So the Django side works great, I'm always able to upload an image successfully as long as I'm logged in. I'm stuck though however on how to POST an image to this extremely simple Django view, I've already logged in to the Django server as a user using a separate NSUrl request:
UIImage *picture = [UIImage imageNamed:#"myFile.png"];
NSData *imageData = UIImagePNGRepresentation(picture);
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
initWithURL:[NSURL
URLWithString:#"https://domfa.de/upload_profile/"]];
[request setHTTPMethod:#"POST"];
[request setValue:#"image/png" forHTTPHeaderField:#"Content-type"];
[request setHTTPBody:imageData];
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSLog(#"%#", returnData);
So assuming all the Django code works, which it does, what's wrong with my objective-c code which does the actual image uploading?
And also, how could I get response messages from the server in the NSLog so I could better know why the server won't accept a file POST request?
Getting feedback what is going on when sending a HTTP request from iOS
The problem is that you are not passing in a pointer to a NSURLResponse object which you could examine after the request was made.
NSURLResponse *response;
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
Now you can examine the output by looking at the object.
Why is it not uploading?
There are several issues with your approach: You are using django's forms API which is OK, but there are two pitfalls in your code:
CSRF You are not setting the the CSRF value correctly when submitting the form. You can workaround it by first accessing your form with a GET request, just like a browser would do it, then get the CSRF token from the cookies which the server set and then sending a POST request including the retrieved CSRF token. See also CSRF handling in AJAX requests
Data encoding You are not sending form data back to the view, but raw data instead. This will not work this way. You would have to send proper POST form data. You could do it manually or use ASIHTTPRequest which abstracts the cumbersome handling of multipart/form-data.
I would rethink the design decision to use a form to programatically upload a picture. Why not use something more like a REST API for this? There are great frameworks for implementing a clean REST API in django (e.g. http://tastypieapi.org/).
Maybe looking at the API description of Twitter and app.net could help inspiring you how to build this.

IOS AFNetworking over 3G is not reliable when fetching files from rackspace

I've encountered a problem using AFNetworking to download files from a Rackspace repository.
Basically sometimes the file is not completelly transfered but AFNetworking fires the success block. Also the http response code received is set to 200 OK.
This situation only happens on a 3G connection. On WiFi we correctly receive all the files.
Can anyone give us any hints on what can be causing this behaviour?
Edit, 2012-05-10
I've detected that the problem may be elsewhere, what I am doing is checking the CRC of the file and compare it to the expected CRC. However (and it only happens when the connection is over 3G) the CRC check fails. Below are some snippets of the code I'm using to download the file and then check it's CRC:
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:urlNS];
AFImageRequestOperation * imageRequestOperation = [AFImageRequestOperation imageRequestOperationWithRequest:request
imageProcessingBlock:nil
cacheName: nil
success: ^(NSURLRequest * request, NSHTTPURLResponse * response, UIImage * image)
{
[self.class postProcessDownloadAfNetworking: request andResponse: response resultImage: image error: nil];
}
failure: ^(NSURLRequest * request, NSHTTPURLResponse * response, NSError * error)
{
[self.class postProcessDownloadAfNetworking: request andResponse: response resultImage: nil error: error];
}];
imageRequestOperation.outputStream = [NSOutputStream outputStreamToFileAtPath:obj.destinationFilename append:NO];
[imageRequestOperation start];
And then in the callback method:
if(error == nil)
{
NSData * data = [NSData dataWithContentsOfFile:imgData.destinationFilename];
uint32_t fileCrcInt = [data crc32];
NSString * fileCrcHex = [NSString stringWithFormat:#"%2X", (int) fileCrcInt] ;
NSString * fileCrcHex2 = [NSString stringWithFormat:#"000000%#", fileCrcHex] ;
fileCrcHex2 = [fileCrcHex2 substringFromIndex:[fileCrcHex2 length]-8];
// Compare CRC
if( [expectedCRC isEqualToString:fileCrcHex2] )
{
(...)
Is it possible that the instruction [NSData dataWithContentsOfFile:] is reading the file before the Output Stream from AFNetworking writes it completely? Maybe I'm encountering some kind of iOS disk cache or something like that.
In the absence of other data, I'll take a wild guess:
Certain file types may be subject to a transparent proxy over 3G, which may alter those files. This is with widely-recognized file types with lossy compression. Basically, if you're trying to download a JPEG the proxy might deliver a smaller JPEG than you expect. The cell provider considers it "close enough", and the doubly-compressed JPEG is smaller (and far uglier). Naturally, the re-compressed image will have a different CRC.
This may be true of other file types as well.
I have read reports of T-Mobile doing this. I think it's been noted with other cell providers, too.
There's two possible fixes:
As noted by Tony Million in a comment below, switching to HTTPS will fix this problem as the proxy will no longer be in the middle of the communication.
If your resource is available via HTTPS, this is a fantastic and simple fix.
If not, you can try adding a HTTP header intended to defeat this. I can't verify it will work for you, as my cell provider doesn't play this particular game:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5
The idea is to add a no-transform cache-control HTTP header to your request. This should disallow the server from making this kind of change.
This would seem to be a Rackspace / server issue. If the server is sending back a 200 response code, then that indicates a successful connection and file/data transfer.
Without more details on the server side of things, we can't really help ya.

SSErrorDomain error code 109. NewsStand download from Amazon S3

While downloading a file from Amazon S3 in iOS. Intermittently NSURLConnectionDownloadDelegate's method didFailWithError: get called and this is what I got when I logged received NSError object
Error Code: 109
Error Domain: SSErrorDomain
Error Description: "Cannot connect to .s3.amazonaws.com"
Searched all the Apple documentation, StackOverflow and other sites but not found anything on this. Today I raised a technical query to Apple also for this using my developer account.
Any idea ?
Update:
So after looking into HTTP response error code (403 Forbidden), I got the idea. It is because of "RequestTimeTooSkewed" error from S3 (The difference between the request time and the current time is too large.). I cross checked it by changing iPad's/Mac's system time by 1 hour and this error is coming immediately now, even for a small (200kb) file.
Now as suggested in many blogs I am first making a HEAD request to AWS as below to get the Date string and not passing the system Date
NSString *awsURL = #"http://s3.amazonaws.com";
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:awsURL]];
[request setHTTPMethod:#"HEAD"];
NSHTTPURLResponse *response;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error: NULL];
NSString *dateString = nil;
if ([response respondsToSelector:#selector(allHeaderFields)]) {
dateString = [[response allHeaderFields] objectForKey:#"Date"];
}
return dateString;
and setting this as Date header in NSMutableURLRequest
[urlRequest setValue:awsDateString forHTTPHeaderField:#"Date"];
This request I am adding to my issue for download
NKAssetDownload *nkAssetDownload = [nkIssue addAssetWithRequest:urlRequest];
Still the same error !!!! It now more crazier than my last situation.
Anyone ?
Update 2
I was able to make request successfully (even the system clock of my iPad is incorrect) by replacing "GMT" with "+0000" in the date string.
Update 3
Still some requests fail with same error which is weird, but I am assuming it is something the NewsStand Framework is messing up.
So it is RequestTimeTooSkewed error and the above code to fetch date from S3 server's head response to add in request does the trick.

Resources