Search code examples
rubyemailgoogle-api-client

Attaching files to emails with google-api-client gem in ruby


I've managed to send emails via Google API thanks to the google-api-client gem:

def send_message(from:, to:, subject:, body:)
  message = RMail::Message.new
  message.header['From'] = from
  message.header['To'] = to
  message.header['Subject'] = subject
  message.body = body
  @service.send_user_message(
    'me',
    upload_source: StringIO.new(message.to_s),
    content_type: 'message/rfc822'
  )
end

Now I'm trying to attach files to the emails, but I couldn't find examples on how to do it. The example included in the gem's repository doesn't explain the case. I've started doing reverse engineering, but after almost the whole day making attempts I've started doing crazy things.

My last attempt was the following:

upload = Google::Apis::Core::UploadIO.new('/path/to/image.png', 'image/png', 'image.png')
file_part = Google::Apis::Core::FilePart.new(nil, upload)
message_object = Google::Apis::GmailV1::Message.new(payload: file_part, raw: 'this is a test body')
service.send_user_message('me', message_object, content_type: 'message/rfc822')

The Email was bounced.

What's the proper way to attach files?


Solution

  • Turns out it was easier than I expected. Here is an example:

    class Client
      def initialize(service)
        @service = service
      end 
    
      def send_message(from:, to:, subject:, body:)
        message = RMail::Message.new
        message.header.set('From', from)
        message.header.set('To', to)
        message.header.set('Subject', subject)
        message.body = [text_part(body), file_part]
    
        @service.send_user_message(
          'me',
          upload_source: StringIO.new(message.to_s),
          content_type: 'message/rfc822'
        )   
      end 
    
      private
    
      def text_part(body)
        part = RMail::Message.new
        part.body = body
    
        part
      end 
    
      def file_part
        part = RMail::Message.new
        part.header.set('Content-Disposition', 'attachment', 'filename' => File.basename('/path/to/image.png'))
        part.body = File.read('/path/to/image.png')
    
        part
      end 
    end 
    

    I'll wait for further responses, maybe there's something I'm not taking into account.