2

My question could be marked as duplicate but after going through tons of solutions I am still not able to figure out where am i going wrong. Please help.

Name of my image is 'image.png'.Please do specify what should be included in BoundaryConstant. I have just copied it from some site.


//    // create request
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
NSURL* requestURL = [NSURL URLWithString:@"http://....."];
[request setURL:requestURL];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
[request setHTTPShouldHandleCookies:NO];
[request setTimeoutInterval:30];
[request setHTTPMethod:@"POST"];
NSString *BoundaryConstant = @"----------V2ymHFg03ehbqgZCaKO6jy";
NSString *filename = @"image";
// set Content-Type in HTTP header
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", BoundaryConstant];
[request setValue:contentType forHTTPHeaderField: @"Content-Type"];

// post body
NSMutableData *body = [NSMutableData data];

// add image data
NSData *imageData = UIImagePNGRepresentation([UIImage imageNamed:@"image.png"]);
if (imageData) {
    [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", BoundaryConstant] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"image.png\"\r\n", filename] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[@"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[NSData dataWithData:imageData]];
    [body appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
}
[body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", BoundaryConstant] dataUsingEncoding:NSUTF8StringEncoding]];

// setting the body of the post to the reqeust
[request setHTTPBody:body];

// set the content-length
NSString *postLength = [NSString stringWithFormat:@"%lu", (unsigned long)[body length]];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];

// set URL
[request setURL:requestURL];

NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *returnString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];

NSLog(@"Ret: %@",returnString);

mars
  • 1,651
  • 2
  • 15
  • 20

1 Answers1

4

There are extra \r\n characters (see point 2 below), but it otherwise looks OK. Without seeing the server code, nor the NSURLResponse object, nor the NSError object, we can't diagnose what might be going wrong.

A few details:

  1. I would not include dashes at the start of the boundary. If nothing else, it makes it really hard to differentiate the leading -- from the actual boundary.

  2. You are writing an extra \r\n before the boundaries. I'd replace

    [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", BoundaryConstant] dataUsingEncoding:NSUTF8StringEncoding]];
    

    with

    [body appendData:[[NSString stringWithFormat:@"--%@\r\n", BoundaryConstant] dataUsingEncoding:NSUTF8StringEncoding]];
    

    And replace

    [body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", BoundaryConstant] dataUsingEncoding:NSUTF8StringEncoding]];
    

    with

    [body appendData:[[NSString stringWithFormat:@"--%@--\r\n", BoundaryConstant] dataUsingEncoding:NSUTF8StringEncoding]];
    

    As it is (in particular, where you added the image data, a \r\n, and then \r\n--%@--\r\n), you're adding an extra two bytes (namely, \r\n) to the end of the uploaded image. This is generally doesn't cause problems (the image generally still works), but it is not correct.

  3. You generally don't have to set the Content-Length header.

  4. Do not use synchronous requests. Use sendAsynchronousRequest.

  5. You are using nil for response and error objects. If you do that, you are extremely limited as to what diagnostics you can perform. You are flying blind. One should always examine the resulting NSURLResponse and NSError objects.

  6. We cannot tell whether this upload would work without seeing the server code. In particular, I find your use of a variable named filename with a value of of image as the name of the field as a little confusing. But if your $_FILES on the server (or if not using PHP, whatever field name its looking for) is looking for image, that's fine. It's just a confused/misleading choice of variable name. I'd probably call that variable fieldName or something like that.

    Most importantly, is $_FILES looking for a field name called image?

  7. You have a line that says:

    [body appendData:[NSData dataWithData:imageData]];
    

    That could just be:

    [body appendData:imageData];
    

    No need to create new NSData.

  8. I'd remove that second call to setURL. That isn't necessary.

  9. You are loading image.png, converting it to a UIImage and then using UIImagePNGRepresentation to get NSData. That can work but is very inefficient. Just load the contents of the file into NSData directly, bypassing UIImage altogether.

    Also, I'd suggest you log an error message if the image is not found. Right now, it will simply skip the step of adding the image (meaning that it will silently send the request, not telling you if there was a problem).

  10. You should watch this request in a tool like Charles. If you're going to get in this level of detail in preparing requests, you should inspect the requests as they go out.

  11. I'd suggest you just use something like AFNetworking which gets you out of these sorts of weeds. If you want to do this yourself, see https://stackoverflow.com/a/24252378/1271826.

Community
  • 1
  • 1
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • thanx bro! In context to point no 6 plz tell me what is passed in a name? Name of my image is image.png so i have just used the word 'image'. Also let me know if BoundaryConstant could be just any constant. Why is it made so long? – mars Apr 15 '15 at 12:17
  • When upload isn't files, there are two "names" that we concern ourselves with. The "field name" is the predetermined identifier that the server code is looking for when files are uploaded. Think of this as part of the web service API. So look at what field name the server is expecting. The "file name", on the other hand, is the name of the file being uploaded and this may change at runtime depending upon which files the user chooses to upload. What's confusing here is that your variable `filename` is actually being used as the field name. Lol. – Rob Apr 15 '15 at 12:46
  • Yes, the boundary string can be any string value, but you want to make sure it's a sequence of characters that will _never_ occur within the field values being sent. If the boundary string was merely "x", you'd have a 1 out of 256 chance that it would occur within any random value. If boundary string is "xx", that chance drops to 1 out of 65,536. If "xxxx", 1 out of 4 billion. Etc. So by making this string fairly long, it makes it astronomically unlikely that it will ever occur within a field value. – Rob Apr 15 '15 at 12:46
  • You should use a field name in your client code that matches the field name in your server code. So what field name does your server code use? – Rob Apr 15 '15 at 13:33
  • finally my code is working. It was fieldname that was causing all the trouble. Thanx Rob for helping me throughout. – mars Apr 16 '15 at 04:51