2

I'm using the OAuthConsumer library by Jon Crosby (google code link)

I have successfully set up my project with a access & request token/secret to make basic authorized http requests with the server (GET). However now I need to create a call that uploads an image to the server...(POST)

As far as i've found out, i need to define the entire httpBody myself (correct me if i'm wrong). I found some sample code somewhere that appends data to the httpBody but I have absolutely no idea what i'm doing. I have done so like this:

// create url request
urlRequest = [[[OAMutableURLRequest alloc]  initWithURL:urlToServerGoesHere 
                                            consumer:authentication.consumer
                                            token:authentication.token
                                            realm:nil
                                            signatureProvider:nil] autorelease];
// apply http method on request
[urlRequest setHTTPMethod:@"POST"];

// define boundary and content-type of file in header of request
NSString* boundary = @"----IMAGE_UPLOAD";
NSString* contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary];
[urlRequest setValue:contentType forHTTPHeaderField:@"Content-Type"];

// Create entire body
NSMutableData* body = [NSMutableData data];

[body appendData:[[NSString stringWithFormat:@"--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[@"Content-Disposition: form-data; name=\"image\"; filename=\"upload.jpg\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[@"Content-Type: image/jpg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[NSData dataWithData:imageData]];
[body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"--%@--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];

[urlRequest setHTTPBody:body];

When applying the httpBody at the end of the code above, an exception is thrown inside the OAMutableRequest. I'm thinking there's something i'm doing wrong or haven't set. It seems to be related with the parameters array which i'm not using...?

*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSArray objectAtIndex:]: index 1 beyond bounds [0 .. 0]' *** Call stack at first throw:(0   CoreFoundation                      0x00ddc5a9 __exceptionPreprocess + 185
1   libobjc.A.dylib                     0x00f30313 objc_exception_throw + 44
2   CoreFoundation                      0x00dd21cc -[__NSArrayI objectAtIndex:] + 236
3   TwooWrapper                         0x0000d310 -[OAMutableURLRequest parameters] + 610
4   TwooWrapper                         0x0000d45d -[OAMutableURLRequest _signatureBaseString] + 39
5   TwooWrapper                         0x0000c619 -[OAMutableURLRequest prepare] + 213
6   TwooWrapper                         0x0000c17c -[OADataFetcher 

Does anyone have some information on how to upload a file through Oauth with this library that i'm using, or any suggestions? would be a big help! thx

Jovan
  • 2,640
  • 2
  • 24
  • 34

2 Answers2

1

After fiddling around with sort order of the code and the body of the http request I managed to make it work. Here's what it looks like:

// create URL from string
NSURL* url = [[NSURL alloc] initWithString:path];

// create url request
urlRequest = [[[OAMutableURLRequest alloc]  initWithURL:url 
                                            consumer:authentication.consumer
                                            token:authentication.token
                                            realm:nil
                                            signatureProvider:nil] autorelease];
[urlRequest prepare];
[urlRequest setHTTPMethod:@"POST"];

// define boundary and content-type of file in header of request
NSString* boundary = @"----IMAGE_UPLOAD";
NSString* contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary];
[urlRequest setValue:contentType forHTTPHeaderField:@"Content-Type"];

// Create entire body
NSMutableData* dataRequestBody = [NSMutableData data];

[dataRequestBody appendData:[[NSString stringWithFormat:@"--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[dataRequestBody appendData:[@"Content-Disposition: form-data; name=\"image\"; filename=\"upload.jpg\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[dataRequestBody appendData:[@"Content-Type: image/jpg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[dataRequestBody appendData:[NSData dataWithData:imageData]];
[dataRequestBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];

// add 'type' parameter to request body
[dataRequestBody appendData:[[NSString stringWithFormat:@"--%@\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\npublic\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];

// Write end-boundary!
[dataRequestBody appendData:[[NSString stringWithFormat:@"--%@--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];

[urlRequest setHTTPBody:dataRequestBody];

// write content length of body in header!
[urlRequest setValue:[[NSNumber numberWithInt:[dataRequestBody length]] stringValue] forHTTPHeaderField:@"Content-Length"];

// do call
dataFetcher = [[OADataFetcher alloc]init];
[dataFetcher fetchDataWithRequest:urlRequest delegate:self didFinishSelector:@selector(onApiSuccess:rawData:) didFailSelector:@selector(onApiFail:rawData:)];

I also added 2 lines of code to add an extra parameter to the httpbody named 'type' with value 'public'. If you look at the data that's being added, fist the boundary is inserted, then some http protocol mumbo jumbo (don't forget the \n\r which are important) and finish with the boundary to mark the end of the parameter.

Also important is to write the length of the content of your body to the http header 'Content-Length', otherwise the request won't send any data.

Hope this helps someone!

Jovan
  • 2,640
  • 2
  • 24
  • 34
  • This seems replacing the previous post parameters. Not working for me. Other post fields has to be included somehow in oauthSignature, and I cant see anything like that here. How did you solve this? – Geri Borbás Sep 20 '12 at 13:13
  • Can the OAAttachment class be used for this? or the OARequestParameter? – Prat Oct 01 '14 at 14:03
  • I try to do same things but for pass JSON data in POST. It give me "signature_invalid" error. Can you please guide me? – girish_pro Feb 12 '16 at 07:41
0

Are you calling [urlRequest prepare] after setting the body?

Call prepare before setting the body. It's a bug/misfeature in the library, in that it is expecting the body to have parameters like foo=bar&firble=mumble, so if your body doesn't look like that it will crash.

Bryan
  • 11,398
  • 3
  • 53
  • 78
  • Still throws an exception when I do [urlRequest prepare] before setting the [urlRequest setHTTPBody:body] – Jovan Oct 06 '11 at 07:40
  • You posted an answer saying "I managed to make it work" where you call prepare before setting the body. – Bryan Oct 06 '11 at 21:32