Rails - Mail, получая тело как обычный текст

Учитывая: message = Mail.new(params[:message])

как показано здесь: http://docs.heroku.com/cloudmailin

В нем показано, как получить message.body как HTML, как вам получить версию plain/text?

Спасибо

Ответ 1

Код выше:

message = Mail.new(params[:message])

создаст новый экземпляр почтового камня из полного сообщения. Затем вы можете использовать любой из методов этого сообщения, чтобы получить контент. Поэтому вы можете получить простой контент, используя:

message.text_part

или HTML с

message.html_part

Эти методы будут только догадываться и находить первую часть в многостраничном сообщении типа text/plain или text/html. CloudMailin также предоставляет эти методы удобства, но через params [: plain] и params [: html]. Стоит помнить, что в сообщении никогда не гарантируется наличие простой или html-части. Возможно, стоит упомянуть следующее:

plain_part = message.multipart? ? (message.text_part ? message.text_part.body.decoded : nil) : message.body.decoded
html_part = message.html_part ? message.html_part.body.decoded : nil

В качестве побочного примечания также важно извлечь кодировку содержимого из сообщения при использовании этих методов и убедиться, что вывод закодирован в желаемый способ кодирования (например, UTF-8).

Ответ 2

Что такое Mail?

Определенный в вопросе message является экземпляром того же класса Mail или Mail::Message, который также используется в ActionMailer::Base или в почтовый камень.

Я не уверен, где это интегрировано в рельсы, но Стив Смит указал, что это определено в почтовый камень.

Извлечение части из многостраничной электронной почты

В окне gem readme есть фрагмент при чтении многостраничных писем.

Помимо методов html_part и text_part, которые просто найдут первую часть соответствующего типа mime, можно получить доступ и петля через детали вручную и фильтровать по критериям по мере необходимости.

message.parts.each do |part|
  if part.content_type == 'text/plain'
    # ...
  elsif part.content_type == 'text/html'
    # ...
  end 
end

Mail::Part описанный здесь.

Проблемы с кодировкой

В зависимости от источника полученной почты могут возникнуть проблемы с кодировкой. Например, рельсы могут идентифицировать неправильный тип кодирования. Если затем попытаться преобразовать тело в UTF-8, чтобы сохранить его в базе данных (body_string.encode('UTF-8')), могут быть ошибки в кодировке, такие как

Encoding::UndefinedConversionError - "\xFC" from ASCII-8BIT to UTF-8

(как в этот вопрос SO).

Чтобы обойти это, можно считывать кодировку из части сообщения и указывать рельсы, какая кодировка была перед кодировкой для UTF-8:

encoding = part_to_use.content_type_parameters['charset']
body = part_to_use.body.decoded.force_encoding(encoding).encode('UTF-8')

Здесь метод decoded удаляет строки заголовков, как показано в разделе кодировки почтового gem readme.

EDIT: проблемы с жестким кодированием

Если есть серьезные проблемы с кодировкой, прежний подход не решает, посмотрите на отличный драгоценный камень charlock_holmes.

После добавления этого драгоценного камня в Gemfile существует более надежный способ преобразования кодировок электронной почты с использованием метода detect_encoding, который добавляется к Strings этим камнем.

Мне было полезно определить метод body_in_utf8 для почтовых сообщений. (Mail::Part также наследуется от Mail::Message.):

module Mail
  class Message
    def body_in_utf8
      require 'charlock_holmes/string'
      body = self.body.decoded
      if body.present?
        encoding = body.detect_encoding[:encoding]
        body = body.force_encoding(encoding).encode('UTF-8')
      end
      return body
    end
  end
end

Резюме

# select the part to use, either like shown above, or as one-liner
part_to_use = message.html_part || message.text_part || message

# readout the encoding (charset) of the part
encoding = part_to_use.content_type_parameters['charset'] if part_to_use.content_type_parameters

# get the message body without the header information
body = part_to_use.body.decoded

# and convert it to UTF-8
body = body.force_encoding(encoding).encode('UTF-8') if encoding

EDIT: Или, определив метод body_in_utf8, как показано выше, то же, что и однострочный:

(message.html_part || message.text_part || message).body_in_utf8

Ответ 3

email = Mail.new(params[:message])
text_body = (email.text_part || email.html_part || email).body.decoded

Я использую это решение на плагине RedmineCRM Helpdesk

Ответ 4

Я считаю, что если вы вызываете message.text_part.body.decoded, вы получите его преобразование в UTF-8 для вас почтовым камнем, документация на нем не будет на 100%.