port5432
port5432

Reputation: 6381

Paperclip without a view

I have paperclip (with S3) working in my application, for audio files. The model definition connects S3 with paperclip.

# attachments
has_attached_file :audio, storage: :s3, s3_credentials: Proc.new{|a| a.instance.s3_credentials}
validates_attachment_content_type :audio, :content_type => [ 'audio/mpeg', 'audio/x-mpeg', 'audio/mp3', 'audio/x-mp3', 'audio/mpeg3', 'audio/x-mpeg3', 'audio/mpg', 'audio/x-mpg', 'audio/x-mpegaudio' ]

I can upload the files via a rails simple_form, using this code:

<%= simple_form_for(@sentence) do |f| %>
  <%= f.error_notification %>
  .
  <%= f.input :audio, as: :file %>
  .   
<% end %>

I would also like to create audio using a background (Resque) process. This code retrieves an audio stream from a web API and attempts to save it to the existing model instance. It does not work.

sentences.each do |sentence|
   sentence.audio = get_audio(sentence.sentence)
   sentence.save
end

Paperclip doesn't seem to know how to handle the audio stream.

 failed: #<Paperclip::AdapterRegistry::NoHandlerError: No handler found for "\xFF\xF3\xC8\xC4\x00\x00\x00\x03H\x00\x00\x00\x00LAME3.99.5\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0

** PROGRESS **

I made some progress: wrote the audio stream to an Tempfile... but now Paperclip is complaining about encoding

 def get_audio_tempfile(target)
   audio = translator.speak "#{target}", :language => "#{@language_cd}", :format => 'audio/mp3', :options => 'MaxQuality'
   tempfile = Tempfile.new("target_temp.mp3")
   tempfile.binmode
   tempfile.write(audio)
   tempfile.close
   tempfile.open
   tempfile
 end

Error:

[paperclip] Content Type Spoof: Filename target_temp.mp320160226-32064- r391y9 (audio/mpeg from Headers, [] from Extension), content type  discovered from file command: audio/mpeg. See documentation to allow this combination.

Upvotes: 2

Views: 162

Answers (2)

port5432
port5432

Reputation: 6381

A summary of how to save an audio stream to Paperclip / S3 without going via the view.

Assuming Paperclip is working and writing to S3, the following steps are needed to upload a file (using Paperclip) from somewhere else than the view (in my case, it is a Resque process).

Why do this: to allow for a fix of foreground and background processing, or to batch upload a lot of data.

model

  # attachments
  has_attached_file :audio, storage: :s3, s3_credentials: Proc.new{|a| a.instance.s3_credentials}
  validates_attachment_content_type :audio, :content_type => [ 'audio/mpeg', 'audio/x-mpeg', 'audio/mp3', 'audio/x-mp3', 'audio/mpeg3', 'audio/x-mpeg3', 'audio/mpg', 'audio/x-mpg', 'audio/x-mpegaudio' ]

view

<%= simple_form_for(@sentence) do |f| %>
  <%= f.error_notification %>
  .
  <%= f.input :audio, as: :file %>
  .   
<% end %>

job

 sentence.audio = get_audio_tempfile(sentence.sentence)
 sentence.save

get_audio_tempfile

def get_audio_tempfile(target)
  audio = translator.speak "#{target}", :language => "#{@language_cd}", :format => 'audio/mp3', :options => 'MaxQuality'
  tempfile = Tempfile.new(['target_temp','.mp3'])
  tempfile.binmode
  tempfile.write(audio)
  tempfile.rewind
  tempfile
end

Important notes:

  • include the correct filename extension
  • rewind the tempfile
  • don't close the tempfile before using it

Thank you to @tobmatth for the help with the file issues.

Upvotes: 0

tobmatth
tobmatth

Reputation: 269

I don't get what exactly your get_audio method is doing, but you need to make sure it returns a file handle, e.g.

sentence.audio = File.new(path_to_your_file, "r")
sentence.save

As for your Tempfile approach, make sure to create it like this

Tempfile.new([ 'foobar', '.mp3' ])

This way PaperClip won't complain about the file extension

Upvotes: 1

Related Questions