Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ios http POST (Data and Image): Image not getting posted

I used the following code to PUSH an image and data into a server. The data is being sent but the image is not received in the server. Can someone spot me if there is an error in my below code which I have used:

NSString *urlString = [[NSString alloc]initWithString:[NSString stringWithFormat:@"%@action=savesign",MainURL]];

// set up the form keys and values (revise using 1 NSDictionary at some point - neater than 2 arrays)
NSArray *keys = [[NSArray alloc] initWithObjects:@"user",@"poll",nil];
NSArray *vals = [[NSArray alloc] initWithObjects:user,pollid,nil];

// set up the request object
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:urlString]];
[request setHTTPMethod:@"POST"];

//Add content-type to Header. Need to use a string boundary for data uploading.
NSString *boundary = @"0xKhTmLbOuNdArY";
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary];
[request addValue:contentType forHTTPHeaderField: @"Content-Type"];

//create the post body
NSMutableData *body = [NSMutableData data];
[body appendData:[[NSString stringWithFormat:@"--%@\r\n",boundary] dataUsingEncoding:NSASCIIStringEncoding]];

//add (key,value) pairs (no idea why all the \r's and \n's are necessary ... but everyone seems to have them)
for (int i=0; i<[keys count]; i++) {
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n",[keys objectAtIndex:i]] dataUsingEncoding:NSASCIIStringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"%@",[vals objectAtIndex:i]] dataUsingEncoding:NSASCIIStringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] dataUsingEncoding:NSASCIIStringEncoding]];
}
[body appendData:[@"Content-Disposition: form-data; name=\"image\"\r\n" dataUsingEncoding:NSASCIIStringEncoding]];
[body appendData:[@"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSASCIIStringEncoding]];
[body appendData:[NSData dataWithContentsOfFile:pngFilePath]];
[body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n",boundary] dataUsingEncoding:NSASCIIStringEncoding]];


NSData *imageData = UIImagePNGRepresentation(_Signfield.image);
    [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"myPngFile.png\"\r\n", _Signfield.image] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[@"Content-Type: image/jpeg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:imageData];
    [body appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];

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

// make the connection to the web
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *returnString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];

NSLog(returnString);

This is the response from the server

{"status":"failure","error":[],"user":0}

And when I login and check the data is there but not the image.

like image 855
Spidy Avatar asked Dec 07 '22 04:12

Spidy


1 Answers

When you perform a file upload via HTTP POST, the data that gets transmitted over the wire looks like this:

POST /upload HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, compress
Content-Length: 17918
Content-Type: multipart/form-data; boundary=0xKhTmLbOuNdArY
Host: example.com
User-Agent: HTTPie/0.7.2

--0xKhTmLbOuNdArY
Content-Disposition: form-data; name="user"

Diphin-Das
--0xKhTmLbOuNdArY
Content-Disposition: form-data; name="poll"

1
--0xKhTmLbOuNdArY
Content-Disposition: form-data; name="image"; filename="myPNGFile.png"
Content-Type: image/png

[Binary PNG image data not shown]
--0xKhTmLbOuNdArY--

The first seven lines are the HTTP headers that describe the request to server, including:

  • The HTTP request method (POST)
  • The resource being requested (/upload)
  • The HTTP protocol version (HTTP/1.1)
  • The length of the request body (Content-Length: 17918)
  • The type of data included in the request body (Content-Type: multipart/form-data; boundary=0xKhTmLbOuNdArY)

That last one is interesting. By setting the content type to multipart/form-data, we're allowed to include a mix of different data types into the request body. The boundary tells the server how each of the form values are separated in the request body.

The form values in the request body are described using a simple structure:

--[boundary marker]
Content-Disposition: form-data; name="[parameter name]"
Content-Type: [parameter value MIME type]

[parameter value]

The Content-Type header is optional if the parameter value is alphanumeric, but for other data types (images, videos, documents, etc.) it's required. The end of request body is signaled by a terminating boundary marker which is a standard boundary marker suffixed with a double-hyphen, e.g. --0xKhTmLbOuNdArY--. New line characters (\r\n) are used to delimit the various elements of the content parts.

There can be other elements to the form values in a multipart POST request. If you're interested, you can read about them in RFC 2388.

In order to upload a file from an Objective-C, you need to craft the request body to that specification outlined above. I've taken the code from your question and refactored it to function correctly & added a few explanatory notes along the way.

NSDictionary *params = @{ @"user": user, @"poll": pollid };
NSData *imageData = UIImagePNGRepresentation(_Signfield.image);

NSString *urlString = [[NSString alloc]initWithString:[NSString stringWithFormat:@"%@action=savesign",MainURL]];

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:urlString]];
[request setHTTPMethod:@"POST"];

NSString *boundary = @"0xKhTmLbOuNdArY";
NSString *kNewLine = @"\r\n";

// Note that setValue is used so as to override any existing Content-Type header.
// addValue appends to the Content-Type header
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary];
[request setValue:contentType forHTTPHeaderField: @"Content-Type"];

NSMutableData *body = [NSMutableData data];

// Add the parameters from the dictionary to the request body
for (NSString *name in params.allKeys) {
    NSData *value = [[NSString stringWithFormat:@"%@", params[name]] dataUsingEncoding:NSUTF8StringEncoding];

    [body appendData:[[NSString stringWithFormat:@"--%@%@", boundary, kNewLine] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"", name] dataUsingEncoding:NSUTF8StringEncoding]];
    // For simple data types, such as text or numbers, there's no need to set the content type
    [body appendData:[[NSString stringWithFormat:@"%@%@", kNewLine, kNewLine] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:value];
    [body appendData:[kNewLine dataUsingEncoding:NSUTF8StringEncoding]];
}

// Add the image to the request body
[body appendData:[[NSString stringWithFormat:@"--%@%@", boundary, kNewLine] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"myPngFile.png\"%@", @"image", kNewLine] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"Content-Type: image/png"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"%@%@", kNewLine, kNewLine] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:imageData];
[body appendData:[kNewLine dataUsingEncoding:NSUTF8StringEncoding]];

// Add the terminating boundary marker to signal that we're at the end of the request body
[body appendData:[[NSString stringWithFormat:@"--%@--", boundary] dataUsingEncoding:NSUTF8StringEncoding]];

[request setHTTPBody:body];

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

NSLog(@"%@", returnString);
like image 111
neilco Avatar answered Dec 28 '22 04:12

neilco