write pdf from dropbox to paperclip - ruby-on-rails

I am attempting to copy pdf files from dropbox to paperclip. Everything seems to work except that the pdf's fail to load when I try and view them from my rails application.
I have paperclip working:
class Document < ActiveRecord::Base
has_attached_file :file, content_type: ["application/pdf", "application/x-pdf"]
validates_attachment_content_type :file, content_type: ["application/pdf", "application/x-pdf", "application/rtf", 'application/x-rtf', 'text/rtf', 'text/plain']
end
To upload the file to paperclip I do the following:
contents = DropboxClient.new(DROPBOX_AUTH_TOKEN).get_file(PATH_TO_MY_PDF)
file = StringIO.new(contents)
file.class.class_eval{ attr_accessor :content_type, :original_filename }
file.content_type = "application/pdf"
file.original_filename = "my_pdf.pdf"
Document.create(file: file)
But when I click on a link that sends to a send_data(#document.file, filename: #document.file_file_name) the file is downloaded but cannot be opened. The downloaded file has content type of pdf and says it's 4kb where as the original is 10kb.
If it's helpful the contents that DropboxClient.new(DROPBOX_AUTH_TOKEN).get_file(PATH_TO_MY_PDF) returns is of the form
\x03Y\x8E\xCB\xC3\x97Zi\xA5\x92\xBB\xF1\xFD\xCD\x1D\xF5\x18\xF4\x95\xEB/\xD3\xAB\xCF\x9D\xB6\x02\x89\xDB\x9E\xE9\xBFJ!pF\xCB\xEA(\xC4B*\
but much longer.

Turns out everything was working fine. But in using send data you need to send the raw data, not the paperclip file object (duh!). Changing to
send_data(Paperclip.io_adapters.for(#document.file).read, filename: #document.file_file_name)
solved my problems.

Related

Rails ActiveStorage Updating attached file after processing

Fairly new to using ActiveStorage (love it so far!), but I am running into an issue with trying to figure out how to update a previously attached item.
I have a front-end form that allows a user to upload a PDF, my backend then takes the PDF and scrapes it for data, then exports the data to an excel spreadsheet. My code (when I call to re-attach the export), I see my item in my specified s3 bucket, but its a hashed key name.
class Export < ApplicationRecord
has_one_attached :item
def process_pdf!
pdf_file_name = "#{item.blob.key}_#{item.blob.filename.to_s}"
xls_file_name = "#{item.blob.key}_#{item.blob.filename.to_s.sub('pdf', '')}"
file = "#{Rails.root}/tmp/#{pdf_file_name}"
File.open(file, 'wb') do |f|
f.write(pdf.download)
end
PdfScraper.call(pdf_file: file, output_name: xls_file_name)
self.item.attach(
io: File.open("#{Rails.root}/tmp/#{xls_file_name}"),
filename: "#{xls_file_name}.xlsx",
content_type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
)
File.delete(file)
true
end
end
How do I attach the generated excel spreadsheet?

How to copy one object from one model to another model with Rails ActiveStorage

I'm currently using Rails 5.2.2.
I'm trying to copy one image between 2 models. So I want 2 files, and 2 entries in blobs and attachments.
My original object/image is on AWS S3.
My original model is photo, my target model is image.
I tried this:
image.file.attach(io: open(best_photo.full_url), filename: best_photo.filename, content_type: best_photo.content_type)
full_url is a method added in photo.rb:
include Rails.application.routes.url_helpers
def full_url
rails_blob_path(self.file, disposition: "attachment", only_path: true)
end
I got this error, as if the file was not found:
No such file or directory # rb_sysopen -
/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBHZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--2f865041b01d2f2c323a20879a855f25f231289d/881dc909-88ab-43b6-8148-5adbf888b399.jpg?disposition=attachment
I tried other different things such (this method is used when displaying images with image_tag() and works correctly:
def download_variant(version)
variant = file_variant(version)
return rails_representation_url(variant, only_path: true, disposition: "attachment")
end
Same error.
I verified and the file is present on the S3 server.
What did I miss ?
Thanks.
You can attach the source blob to the target:
image.file.attach best_photo.file.blob
Updated for Rails 6+ - this worked for me
image.file.attach(io: StringIO.new(best_photo.download),
filename: best_photo.filename,
content_type: best_photo.content_type)
OK, I got it.
I used service_url:
image.file.attach(io: open(best_photo.file_variant("large").service_url), filename: best_photo.file.blob.filename, content_type: best_photo.file.blob.content_type)
It's possible to use file_blob instead of file.blob
You can copy using update
image.update(file: best_photo.file_blob)
or attach
image.file.attach(best_photo.file_blob)
Both methods actually just create new blob association, there is no physical copying of attachment. So be careful when you call image.blob.purge or if you have dependent: :purge_later
If you want real copy you need to read attachment with ActiveStorage::Blob#download
image.file.attach(
io: StringIO.new(best_photo.file.download),
filename: best_photo.file.filename,
content_type: best_photo.file.content_type
)
or with ActiveStorage::Blob#open
best_photo.file_blob.open do |tempfile|
image.file.attach(
io: tempfile,
filename: best_photo.file.filename,
content_type: best_photo.file.content_type
)
end
Be careful with large files

Paperclip Direct to S3 Invalid Content Type

When I upload my file directly to S3, take that URL and pass it to Paperclip for saving in the model, Paperclip detects the incorrect content_type of application/octet-stream. Why?
My controller method looks something like this:
def update
#model.update({
video: URI.parse(URI.unescape(clip_params[:video_url])),
thumbnail: URI.parse(URI.unescape(clip_params[:thumbnail_url]))
})
end
and I validate content_type in my model like this:
validates_attachment :video, content_type: { content_type: 'video/mp4' }
validates_attachment :thumbnail, content_type: { content_type: 'image/png' }
yet I still receive an error from Paperclip stating that the content_type of my file is invalid. Why is it not reading the correct content type when I pass in a URL?

rename and download amazon file ruby mongoid

When I click a link, the amazon file is downloading fine. I have thousands of pdf files in amazon. Now mongoid id as the name of that amazon files. When I download the the files its save with that id's. But I would like to rename the file before save that file to local. I have name field in my mongoid.
include Mongoid::Paperclip
has_mongoid_attached_file :profile_doc
when I link a like this controller action will call
def download
if #company.send(doc).exists?
redirect_to #company.send(doc).expiring_url
else
respond_with_error(:not_found)
end
end
How I rename the file when download the file. now file is downloading like this 50sfdkkmzd.pdf I would like to save name.pdf.
Thanks for your time.
try this, it should work fine
def download
if #company.send(doc).exists?
data = open(#company.send(doc).expiring_url)
send_data data.read, :filename => "name.pdf", :type => data.content_type
else
respond_with_error(:not_found)
end
end

setting content-disposition for docx using paperclip

Iam trying to add Content-Disposition to my docx files in s3. Something along the lines of: Content-Disposition: attachment; filename="filename.docx". I want to do this because IE (< 9) downloads docx files as zip files. After some googling I found that there is a workaround for this, by adding a content-disposition to the content as well. I tried using the before_post_process call back and did
before_post_process :set_content_disposition
def set_content_disposition
filename = self.attachment.instance.attachment_file_name
self.attachment.instance_write(:content_disposition, "attachment; filename="+filename)
end
But, it still downloads as zip file. Is there a way to correctly do this.
Prozac's answer (using before_post_process to edit the options) didn't work for me. However, there's now a simpler method, anyway. You can pass a proc directly to the :s3_headers key in the options hash of your has_attached_file call:
has_attached_file :attachment, {
...,
:s3_headers => lambda { |attachment|
# pass whatever you want in place of "attachment.name"
{ "Content-Disposition" => "attachment; filename=\"#{attachment.name}\"" }
},
...
}
I finally found a way .. there is a before_post_process callback with paperclip gem.
we can do something like this..
has_attached_file :sample
before_post_process :set_content_dispositon
def set_content_dispositon
self.sample.options.merge({:s3_headers => {"Content-Disposition" => "attachment; filename="+self.sample_file_name}})
end
I can't help you with paperclip but the correct MIME Type/Content Type for docx files is application/vnd.openxmlformats-officedocument.wordprocessingml.document.
Using that will stop IE downloading them as zip files.
Here are all the MIME types for the new office formats.
Extension MIME Type
.xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
.xltx application/vnd.openxmlformats-officedocument.spreadsheetml.template
.potx application/vnd.openxmlformats-officedocument.presentationml.template
.ppsx application/vnd.openxmlformats-officedocument.presentationml.slideshow
.pptx application/vnd.openxmlformats-officedocument.presentationml.presentation
.sldx application/vnd.openxmlformats-officedocument.presentationml.slide
.docx application/vnd.openxmlformats-officedocument.wordprocessingml.document
.dotx application/vnd.openxmlformats-officedocument.wordprocessingml.template
.xlam application/vnd.ms-excel.addin.macroEnabled.12
.xlsb application/vnd.ms-excel.sheet.binary.macroEnabled.12
Prozac,
I think that in Paperclip you have to set s3_header['Content-Disposition'] hash but I'm in the same issue given that s3_header hash is not interpolated I still can't figure out how to put the filename there without patch Paperclip
check this solution http://groups.google.com/group/paperclip-plugin/browse_thread/thread/bff66a0672a3159b

Resources