Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I send a photo email attachment in iOS using POST request to SendGrid?

Tags:

php

ios

sendgrid

I am using NSMutableURLRequest to send POST data to a server-side PHP script that sends emails using SendGrid. This works just fine. However, I have no idea how to package UIImagedata properly to send as an attachment. Here is my current code:

// Choose an image from your photo library
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
    chosenImage = info[UIImagePickerControllerEditedImage]; // chosenImage = UIImage
    pickedData = UIImagePNGRepresentation(chosenImage);  // pickedData = NSData
    attachment = TRUE;
    [picker dismissViewControllerAnimated:YES completion:NULL];
}

-(void)sendMail {
    toEmailAddress = @"[email protected]";
    subject = @"Some Subject";
    message = @"Some message...";
    fullName = @"Mister Bla Bla";

    if (attachment == TRUE) {
        // Create NSData object as PNG image data from camera image
        NSString *picAttachment = [NSString stringWithFormat:@"%lu",(unsigned long)[pickedData length]];
        NSString *picName = @"Photo";
        post = [NSString stringWithFormat:@"&toEmailAddress=%@&subject=%@&message=%@&fullName=%@&picAttachment=%@&picName=%@", toEmailAddress, subject, message, fullName, picAttachment, picName];
    } else {
        post = [NSString stringWithFormat:@"&toEmailAddress=%@&subject=%@&message=%@&fullName=%@", toEmailAddress, subject, message, fullName];
    }

    NSData * postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:NO];
    NSString * postLength = [NSString stringWithFormat:@"%lu",(unsigned long)[postData length]];
    NSMutableURLRequest * request = [[[NSMutableURLRequest alloc] init] autorelease];
    [request setURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://www.someURL.com/sendgrid.php"]]];
    [request setHTTPMethod:@"POST"];
    [request setValue:postLength forHTTPHeaderField:@"Content-Length"];
    [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
    [request setHTTPBody:postData];

    // send the POST request, and read the reply by creating a new NSURLSession:
    NSURLSession *conn = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    [[conn dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        NSString *requestReply = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
        NSLog(@"requestReply: %@", requestReply); // Return response from PHP script on server.
    }] resume];
}

If you research this question, you may find that there is an existing iOS SendGrid lib. Unfortunately, this is not the answer. The folks at SendGrid tell me not to use the lib because of a security concern.

like image 589
Marsman Avatar asked Aug 11 '16 16:08

Marsman


1 Answers

New answer:

To upload file as email attachement directly to SendGrid you should use Web API v3 and create request as described in docs.

First of all, you need to add authentication header to your request. Secondly, you need to convert your data to JSON format. If we are talking about files, you need to encode you file data with Base64, as described in body parameters section:

JSON PARAMETER: attachements/content
TYPE: string
REQUIRED: Yes
The Base64 encoded content of the attachment.

Also, take a look to disposition and content_id params: they will help you to setup your file appearance in mail.

Old answer:

The standard way of uploading both parameters and files is using POST request with multipart messages. I have change your code to create data of this format:

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
  UIImage* image = info[UIImagePickerControllerEditedImage];
  NSDictionary* params = @{
    @"toEmailAddress" : @"[email protected]",
    @"subject" : @"Some Subject",
    @"message" : @"Some message...",
    @"fullName" : @"Mister Bla Bla",
  };
  [picker dismissViewControllerAnimated:YES completion:^{
    [self sendMailWithParams:params image:image];
  }];
}

static NSStringEncoding const kEncoding = NSUTF8StringEncoding;

- (void)sendMailWithParams:(NSDictionary*)params image:(UIImage*)image {
  NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
  request.URL = [NSURL URLWithString:@"http://www.someURL.com/sendgrid.php"];
  request.HTTPMethod = @"POST";

  NSString *boundary = [NSUUID UUID].UUIDString;

  // define POST request as multipart
  NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
  [request addValue:contentType forHTTPHeaderField: @"Content-Type"];

  // prepare boundary
  NSString *middleBoundary = [NSString stringWithFormat:@"--%@\r\n", boundary];
  NSData *middleBoundaryData = [middleBoundary dataUsingEncoding:kEncoding];

  NSMutableData* body = [NSMutableData data];

  // append params
  [params enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSString* value, BOOL* stop) {
    [body appendData:middleBoundaryData];
    NSData* fieldData = [self dataForKey:key value:value];
    [body appendData:fieldData];
  }];

  // append image
  if (image) {
    [body appendData:middleBoundaryData];
    NSData* imageData = [self dataForImage:image imageName:@"photo.png"];
    [body appendData:imageData];
  }

  // add last boundary
  NSString* lastBoundary = [NSString stringWithFormat:@"--%@--\r\n", boundary];
  NSData* lastBoundaryData = [lastBoundary dataUsingEncoding:kEncoding];
  [body appendData:lastBoundaryData];

  // set body to request
  request.HTTPBody = body;

  // add length of body
  NSString *length = [NSString stringWithFormat:@"%llu", (uint64_t)body.length];
  [request setValue:length forHTTPHeaderField:@"Content-Length"];

  // send request
  NSURLSession* session = [NSURLSession sharedSession];
  NSURLSessionDataTask* task = [session dataTaskWithRequest:request
                                          completionHandler:^(NSData *data,
                                                              NSURLResponse *response,
                                                              NSError *error) {
    // handle as you want
  }];
  [task resume];
}

- (NSData*)dataForImage:(UIImage*)image imageName:(NSString*)imageName {
  NSString* fieldDescription = [NSString stringWithFormat:
      @"Content-Disposition: form-data; name=\"image\"; filename=\"%@\"\r\n"
      @"Content-Type: image/png\r\n\r\n", imageName];

  NSMutableData *data = [NSMutableData data];
  [data appendData:[fieldDescription dataUsingEncoding:kEncoding]];

  NSData* imageData = UIImagePNGRepresentation(image);
  [data appendData:imageData];

  NSString* newLine = @"\r\n";
  NSData* newLineData = [newLine dataUsingEncoding:kEncoding];
  [data appendData:newLineData];

  return data;
}

- (NSData*)dataForKey:(NSString*)key value:(NSString*)value {
  NSString* fieldDescription = [NSString stringWithFormat:
      @"Content-Disposition: form-data; name=\"%@\"\r\n\r\n"
      @"%@\r\n", key, value];
  return [fieldDescription dataUsingEncoding:kEncoding];
}

You can use $_POST and $_FILES variables to get access to loaded data in your PHP script. If you want to learn more about multipart messages, check docs here.

like image 183
Roman Ermolov Avatar answered Oct 28 '22 05:10

Roman Ermolov