31

I'm using Paperclip / S3 for file uploading. I upload text-like files (not .txt, but they are essentially a .txt). In a show controller, I want to be able to get the contents of the uploaded file, but don't see contents as one of its attributes. What can I do here?

attachment_file_name: "test.md", attachment_content_type: "application/octet-stream", attachment_file_size: 58, attachment_updated_at: "2011-06-22 01:01:40"

PS - Seems like all the Paperclip tutorials are about images, not text files.

joshfindit
  • 611
  • 6
  • 27
Geoff
  • 9,470
  • 13
  • 52
  • 67

7 Answers7

102

In Paperclip 3.0.1 you could just use the io_adapter which doesn't require writing an extra file to (and removing from) the local file system.

Paperclip.io_adapters.for(attachment.file).read
jwadsack
  • 5,708
  • 2
  • 40
  • 50
  • I see that actually this approach writes to local file system with the next log message: `[paperclip] copying uploads/model/documents/1/Image1.jpg to local file /tmp/2fa67f482133f1c934235b73c2a0395420161123-27627-1xc7ieo.jpg` – Tensho Nov 23 '16 at 09:05
  • @Tensho it looks like you are right for most adapters. If you want to avoid writing to disk you can use a `StringIOAdapter`: https://github.com/thoughtbot/paperclip/blob/master/lib/paperclip/io_adapters/stringio_adapter.rb – jwadsack Dec 01 '16 at 00:44
  • @jwadsack, what do I need to do to use a `StringIOAdapter` over the default `FileAdapter`? – atreat Mar 22 '17 at 16:13
  • @atreat I was wrong. The adapters determine how the data is loaded. Even `StringIOAdapter` [copies to a temporary file](https://github.com/thoughtbot/paperclip/blob/5202acbf4b642230bef71a8dd534bcca11fb4373/lib/paperclip/io_adapters/stringio_adapter.rb#L15). My original response meant that you aren't adding your own local file write on top of what Paperclip does. – jwadsack Mar 24 '17 at 18:51
  • thanks for the response, thats what I was finding in my investigation as well. I chose to go this route because a single file write which I can then read immediately into memory is better than managing my own file pointers. – atreat Mar 27 '17 at 21:08
6

@jon-m answer needs to be updated to reflect the latest changes to paperclip, in order for this to work needs to change to something like:

class Document

  has_attached_file :revision

  def revision_contents(path = 'tmp/tmp.any')
    revision.copy_to_local_file :original, path
    File.open(path).read
  end
end

A bit convoluted as @jwadsack mentioned using Paperclip.io_adapters.for method accomplishes the same and seems like a better, cleaner way to do this IMHO.

ESX
  • 71
  • 1
  • 2
5

To access the file you can use the path method: csv_file.path http://rdoc.info/gems/paperclip/Paperclip/Attachment#path-instance_method

This can be used along with for example the CSV reader.

Shaun McDonald
  • 6,436
  • 2
  • 25
  • 23
  • The return from csv_file.path is no longer a valid reference on the file system, at least for local storage. It now returns the path after the root... it returns /class_name/attachment_name/blah/blah rather than /rails_root/public/system/class_name/attachment_name/blah/blah – Jason Oct 22 '15 at 19:55
  • Hmm, `csv_file.path` worked great for me for local file-system storage in Paperclip 4.3.2. – aec Feb 10 '16 at 20:53
3

Here's how I access the raw contents of my attachment:

class Document

  has_attached_file :revision

  def revision_contents
    revision.copy_to_local_file.read
  end

end

Please note, I've omitted my paperclip configuration options and any sort of error handling.

Jon M.
  • 3,483
  • 1
  • 20
  • 18
  • 8
    New in 3.0.1: * API CHANGE: #to_file has been removed. Use the #copy_to_local_file method instead. – RocketR Jan 14 '13 at 13:01
  • 8
    This copy_to_local_file mentioned above doesn't work like that, as it expects 2 arguments for the style and the local destination path. http://rdoc.info/gems/paperclip/Paperclip/Storage/Filesystem:copy_to_local_file – Shaun McDonald Dec 13 '13 at 15:53
  • 1
    @jwadsack seems much better given the API change – John Naegle Jun 26 '14 at 17:09
  • 2
    You can do e.g: `copy_to_local_file(nil, 'path_name')` The `style` is for thumbnails and can be left nil to download the literal file, though this is a bit undocumented.. – Rich Apr 03 '18 at 20:58
1

You would need to load the contents of the file (using Rubys File.open) into a variable before you show it. This may be an expensive operation if your app gets lots of use, so it may be worthwhile reading the contents of the file and putting it into a text column in your database after uploading it.

robzolkos
  • 2,196
  • 3
  • 30
  • 47
1

Attachment already inherits from IOStream. http://rdoc.info/github/thoughtbot/paperclip/master/Paperclip/Attachment

So it should just be "#{attachment}" or <% RDiscount.new(attachment).to_html %> or send_data(attachment). However you wanted to display the data.

Tim Snowhite
  • 3,736
  • 2
  • 23
  • 27
1

This is a method I used for upload from paperclip to active storage and should provide some guidance on temporarily working with a file in memory. Note: This should only be used for relatively small files.

Written for gem paperclip 6.1.0

Where I have a simple model

class Post
  has_attached_file :image
end

Working with a temp file in ruby so we do not have to worry about closing the file

  Tempfile.create do |tmp_file|
    post.image.copy_to_local_file(nil, tmp_file.path)

    post.image_temp.attach(
      io: tmp_file,
      filename: post.image_file_name,
      content_type: post.image_content_type
    )
  end
codster
  • 71
  • 4