Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Atomic, asynchronous HTTP file POST with sensible feedback?

I've just started development on Macs and have found Cocoa to be a useful and thoughtful framework, but its HTTP functionality has me puzzled.

I have an NSURLConnection object to download a file from my webserver using the HTTP GET method. NSURLConnect's asynchronous connection is great, I get plenty of feedback, I get each chunk received as a new NSData object that I can use to atomically rebuild the file on the client end and, importantly, provide the user with a progress report: [myData length].

Uploads, however, are nowhere near as neat. You can either stick a synchronous request in its own thread or call an asynchronous request (which I believe spawns its own thread), but neither provide you with any useful feedback. There's no delegates to request data or even let me know when data is being sent. Presumably this limits me to files smaller than available memory.

My question is, therefore, is there a simple and elegant solution to HTTP POST file uploads using Cocoa that provides a good deal of feedback and the ability to read files part-by-part, rather than all at once? Or should I write my own class from low-level networking functionality?

Thanks!

like image 437
Dani Avatar asked May 10 '09 23:05

Dani


2 Answers

You may want to look at the ASIHTTPRequest framework. I haven't used it for uploading but it looks like it has more feedback and the usage is pretty straightforward.

like image 94
bbrown Avatar answered Oct 21 '22 00:10

bbrown


I decided to go with CFNetwork functions instead of NSURLConnection. There appears to be a bit more flexibility in async notifications and in specific features (authentication for instance). Unfortunately it's a bit more complicated (run loops for instance blow my mind) so I recommend you read the CFNetwork reference guide if you go this route:

http://developer.apple.com/documentation/Networking/Conceptual/CFNetwork/Introduction/Introduction.html

Here's a snippet of code from my POST routine, FWIW:

// Create our URL
CFStringRef url = CFSTR("Submit");
CFURLRef myURL = CFURLCreateWithString(kCFAllocatorDefault, url, baseUrl);

// Create the message request (POST)
CFStringRef requestMethod = CFSTR("POST");
CFHTTPMessageRef myRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, requestMethod, myURL, kCFHTTPVersion1_1);

// Connect the read socket to the HTTP request stream
CFReadStreamRef myReadStream = CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault, myRequest, readStream);
// TODO: why does this have to be done?
succ &= CFReadStreamSetClient(myReadStream,
  kCFStreamEventOpenCompleted | kCFStreamEventCanAcceptBytes | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
  (CFReadStreamClientCallBack) &MyReadCallBack, &myClientContext);
CFReadStreamScheduleWithRunLoop(myReadStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
succ &= CFReadStreamOpen(myReadStream);

like image 42
sehugg Avatar answered Oct 21 '22 01:10

sehugg