Alexander
Alexander

Reputation: 4287

Rails 4.2 - Send Image Data as Email Attachment Without Saving Image

My client app is an iOS app written in Swift. In that iOS app, I convert an image to a Base64 encoded String and then send this String to my Rails server in the body of an HTTP request. This is my code for doing so:

// Create a new URL request with the URL
let request: NSMutableURLRequest = NSMutableURLRequest(URL: url)

// Set the HTTP method to POST
request.HTTPMethod = "POST"

// Set the content type of the request to XML
request.setValue("application/xml", forHTTPHeaderField: "Content-Type")

// Convert the image to binary and then to a Base 64 Encoded String
let imageData:String = UIImagePNGRepresentation(image).base64EncodedStringWithOptions(nil)

// Set the HTTP Body of the request to the image
request.HTTPBody = NSData(base64EncodedString: imageData, options: nil)

In my Rails controller handling the request, I would like to retrieve the image and send it as an email attachment. I do NOT want to save the image anywhere; I just want to decode the image in memory and then somehow use that as the email attachment.

How can I retrieve the image data in my Rails controller and decode it in a way that would allow me to send it back as an email attachment?

To retrieve the image data, I've tried using request.body.read, but this returns an empty String for some reason.

Thanks in advance for any help!

EDIT:

request.body.read was returning an empty String because I used a GET request. I've since learned that it's not a good idea to send an HTTP Body in a GET request, so I changed the method to POST. Now request.body.read is returning my encoded String! I've also added the Content-Type header to the request.

Still, I can't figure out how to properly decode the HTTP Body and assign it to an image object of some sort.

EDIT #2:

I've managed to send the email attachment in my mailer using the following code:

attachments["file.png"] =
{
   mime_type: 'image/png',
   content: Base64.decode64(request.body.read)
}

Unfortunately, the PNG file cannot be opened when I receive it in the email. I don't know if the encoding translates well from Swift to Ruby. I'll keep investigating.

EDIT #3:

I removed the Base64 String encoding, and it's working great! See my posted answer below.

Upvotes: 1

Views: 1373

Answers (1)

Alexander
Alexander

Reputation: 4287

I figured it out! I think the Base64 encoding/decoding isn't performed the same way on Swift and Ruby, so I decided to send the image as NSData without String encoding and then send it back, and that worked! Here is my final code:

Swift

// Create a URL
let url:NSURL = NSURL(string: urlString)!

// Create a new URL request with the URL
let request: NSMutableURLRequest = NSMutableURLRequest(URL: url)

// Set the HTTP method to POST
// GET requests usually do not have an HTTP Body, and it's considered a very bad idea to include one in a GET request
request.HTTPMethod = "POST"

// Set the content type of the request to XML
request.setValue("application/xml", forHTTPHeaderField: "Content-Type")

// Convert the image to binary data
let imageData:NSData = UIImagePNGRepresentation(image)

// Set the HTTP Body of the request to the image
request.HTTPBody = imageData

// Create a new queue
let queue:NSOperationQueue = NSOperationQueue()

// Send the async request
NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:
{ (response:NSURLResponse!, data:NSData!, error:NSError!) -> Void in
    println(response)
})

You can put this code in any method.

Ruby on Rails (Ruby 2.1.1 and Rails 4.2.0)

class ImageApiController < ApplicationController
  # Skip verifying the authenticity token since a form wasn't submitted; a web request was sent
  skip_before_filter :verify_authenticity_token, :only => [ :index ]

  def index
    params[:image] = request.body.read

    # Send the email
    UserMailer.image_attachment_email(params).deliver_now

    respond_to do |format|
      format.html
      format.js  { render plain: "Test" }
      format.xml { render plain: "Test" }
    end
  end
end

Then in UserMailer:

class UserMailer < ApplicationMailer
  def image_attachment_email(params)
    attachments["image.png"] =
    {
      mime_type: 'image/png',
      content: params[:image]
    }

    # Send an email
    mail(to: "[email protected]", subject: "Image")
  end
end

So actually no Base64 String encoding was needed. This code works great!

Upvotes: 3

Related Questions