Can't upload image to active storage - ruby-on-rails

I am unabe to store a base64 file using ActiveStorage,I am recieving a base64 string from my client
params["image"] = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAREAAABMCAYAAABK84MTAAAABHNCSVQICAgIfAhkl0RVh"
and when I try to attach it I get:
ActiveSupport::MessageVerifier::InvalidSignature(ActiveSupport::MessageVerifier::InvalidSignature):
I've followed many tutorials, tried decoding it first:
decoded_image = Base64.decode64(params["image"])
post.image.attach(decoded_image)
As well as removing the data:image/png;base64 part from the string with:
decoded_image = Base64.decode64(params["image"]['data:image/png;base64,'.length .. -1])
And then attaching the image with no success, when I do it directly from a file with:
file = open("image.png")
post.image.attach(io: file, filename: "post.png")
It works perfectly, so I think that my mistake is during the parsing of the string

I'm not sure if it will work, but give a try to this approach creating a temporary file with Tempfile:
encoded_image = params["image"]['data:image/png;base64,'.length .. -1]
decoded_image = Base64.decode64(encoded_image)
file = Tempfile.new
file.binmode
file.write(decoded_image)
file.rewind
post.image.attach(
io: file,
filename: "post.png" # The name of the file should be received from paramaters, as well
)
file.close
file.unlink

Related

Confusion about creating and writing to File in Ruby on rails

I am trying to create a new file and then write some content to it just to create a basic backup of a template.
When I log out the values of filename and file_content they are correct, but when I send the data all I get is a file named after the method (download_include) and a fixnum inside the file, the last one made was 15.
# POST /download_include/:id
def download_include
#include = Include.find(params[:id])
version_to_download = #include.latest_version_record
filename = "#{version_to_download.name}"
file_content = "#{version_to_download.liquid_code.to_s}"
file = File.open(filename, "w") { |f| f.write (file_content) }
send_data file
end
I also tried send_file but that produces the error
no implicit conversion of Fixnum into String
I also tried to just write dummy values like below, and it still produced a file named after the method with a fixnum inside it.
file = File.open("DOES THIS CHANGE THE FILENAME?", "w") { |f| f.write ("FILE CONTENT?") }
I feel I am missing something obvious but I cannot figure it out after looking at many examples here and in blogs.
If you don't end along the filename as an option for send_data, it defaults to the method name.
Secondly, the download wants to read the data from a buffer. My guess is your syntax is just sending a file handle.
Try this...
send_data(file.read, filename: filename)
Or skip the intermediate file and try...
send_data(version_to_download.liquid_code.to_s, filename: filename)

How to convert word file to PDF in ROR

I am using Libreconv gem to convert word to doc but it's not working with S3
bucket = Aws::S3::Bucket.new('bucket-name')
object = bucket.object file.attachment.blob.key
path = object.presigned_url(:get)
Libreconv.convert(path, "public/test.pdf")
If I try to convert this path to PDF using Libreconv then it's give me filename too long error. I have wrriten this code under ActiveJobs. So kindly provide me solutions as per ActiveJobs.
Can someone please suggest me how can I convert word file to pdf.
Here path is https://domain.s3.amazonaws.com/Bf5qPUP3znZGCHCcTWHcR5Nn?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIZ6RZ7J425ORVUYQ%2F20181206%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20181206T051240Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host&X-Amz-Signature=b89c47a324b2aa423bf64dfb343e3b3c90dce9b54fa9fe1bc4efa9c248e912f9
and error I am getting is
Error: source file could not be loaded
*** Errno::ENAMETOOLONG Exception: File name too long # rb_sysopen - /tmp/Bf5qPUP3znZGCHCcTWHcR5Nn?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIZ6RZ7J425ORVUYQ%2F20181206%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20181206T051240Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host&X-Amz-Signature=b89c47a324b2aa423bf64dfb343e3b3c90dce9b54fa9fe1bc4efa9c248e912f9.pd
It seems that you PDF is created with all the params needed to fetch docx from S3.
I suppose it happens in this line:
target_tmp_file = "#{target_path}/#{File.basename(#source, ".*")}.#{File.basename(#convert_to, ":*")}"
#source is https://domain.s3.amazonaws.com/Bf5qPUP3znZGCHCcTWHcR5Nn?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIZ6RZ7J425ORVUYQ%2F20181206%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20181206T051240Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host&X-Amz-Signature=b89c47a324b2aa423bf64dfb343e3b3c90dce9b54fa9fe1bc4efa9c248e912f9 and
> File.basename(#source, ".*")
=> "Bf5qPUP3znZGCHCcTWHcR5Nn?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIZ6RZ7J425ORVUYQ%2F20181206%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20181206T051240Z&X-Amz-Expires=900&X-Amz-SignedHeaders=host&X-Amz-Signature=b89c47a324b2aa423bf64dfb343e3b3c90dce9b54fa9fe1bc4efa9c248e912f9"
As a result Libreconv gem tries to create a tmp file with this long name and it's too long - that's why an error is raised.
Possible solution: split the process into separate steps of fetching file and converting it. Something like:
require "open-uri"
bucket = Aws::S3::Bucket.new('bucket-name')
object = bucket.object file.attachment.blob.key
path = object.presigned_url(:get)
doc_file = open(path)
begin
Libreconv.convert(doc_file.path, "public/test.pdf")
ensure
doc_file.delete
end
following is the answer using combine pdf gem
tape = Tape.new(file)
result = tape.preview
tempfile = Tempfile.new(['foo', '.pdf'])
File.open(tempfile, 'wb') do |f|
f.write result
end
path = tempfile.path
combine_pdf(path)
and for load file for S3 I have used
object = #bucket.object object_key
path = object.presigned_url(:get)
response = Net::HTTP.get_response(URI.parse(path)).body

Writing to .zip file from binary data

I am trying to write a rails test (Using Capybara & Poltergeist) to test .zip file download functionality.
I have the binary data of a .zip file being returned from an XHR request and I am hoping to write this data into a .zip file locally and carry out further tests from there.
The following method emulates a click on a button which, when in-app, returns a zip file of all the files that have been selected:
# Perform XHR
def download_file(link)
page.execute_script("window.downloadFile = function(){ var url = window.location.protocol + '//' + window.location.host + '#{link}'; return getFile(url); }")
page.execute_script("window.getFile = function(url){ var xhr = new XMLHttpRequest(); xhr.open('GET', url, false); xhr.responseType = 'blob'; xhr.send(); return xhr.response; }")
begin
file = page.evaluate_script('downloadFile()')
rescue
raise "Error during XHR. Is url valid?"
end
file
end
I am trying to write the response to file here:
file = download_file(url)
file_path = "#{Rails.root}/tmp/files/download.zip"
File.open(file_path, 'wb'){ |f| f.write file }
When trying to unzip the resulting file using unzip tmp/files/download.zip I'm given the following response:
Archive: tmp/files/download.zip
caution: zipfile comment truncated
error [tmp/files/download.zip]: missing 3182550208 bytes in zipfile
(attempting to process anyway)
error [tmp/files/download.zip]: start of central directory not found;
zipfile corrupt.
(please check that you have transferred or created the zipfile in the
appropriate BINARY mode and that you have compiled UnZip properly)
I have tried overriding the MIME type to text/plain, application/zip etc. but to no avail.
Any suggestions?
I extended the Tempfile class to include a write binary function
class Tempfile
def openBinary
#tmpfile.close if #tmpfile
#tmpfile = File.open(#tmpname, 'wb')
#data[1] = #tmpfile
__setobj__(#tmpfile)
end
end
This made it possible for my system to parse zip files and then read the using Zip::ZipFile class.

How to write to tmp file or stream an image object up to s3 in ruby on rails

The code below resizes my image. But I am not sure how to write it out to a temp file or blob so I can upload it to s3.
origImage = MiniMagick::Image.open(myPhoto.tempfile.path)
origImage.resize "200x200"
thumbKey = "tiny-#{key}"
obj = bucket.objects[thumbKey].write(:file => origImage.write("tiny.jpg"))
I can upload the original file just fine to s3 with the below command:
obj = bucket.objects[key].write('data')
obj.write(:file => myPhoto.tempfile)
I think I want to create a temp file, read the image file into it and upload that:
thumbFile = Tempfile.new('temp')
thumbFile.write(origImage.read)
obj = bucket.objects[thumbKey].write(:file => thumbFile)
but the origImage class doesn't have a read command.
UPDATE: I was reading the source code and found this out about the write command
# Writes the temporary file out to either a file location (by passing in a String) or by
# passing in a Stream that you can #write(chunk) to repeatedly
#
# #param output_to [IOStream, String] Some kind of stream object that needs to be read or a file path as a String
# #return [IOStream, Boolean] If you pass in a file location [String] then you get a success boolean. If its a stream, you get it back.
# Writes the temporary image that we are using for processing to the output path
And the s3 api docs say you can stream the content using a code block like:
obj.write do |buffer, bytes|
# writing fewer than the requested number of bytes to the buffer
# will cause write to stop yielding to the block
end
How do I change my code so
origImage.write(s3stream here)
http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/S3/S3Object.html
UPDATE 2
This code successfully uploads the thumbnail file to s3. But I would still love to know how to stream it up. It would be much more efficient I think.
#resize image and upload a thumbnail
smallImage = MiniMagick::Image.open(myPhoto.tempfile.path)
smallImage.resize "200x200"
thumbKey = "tiny-#{key}"
newFile = Tempfile.new("tempimage")
smallImage.write(newFile.path)
obj = bucket.objects[thumbKey].write('data')
obj.write(:file => newFile)
smallImage.to_blob ?
below code copy from https://github.com/probablycorey/mini_magick/blob/master/lib/mini_magick.rb
# Gives you raw image data back
# #return [String] binary string
def to_blob
f = File.new #path
f.binmode
f.read
ensure
f.close if f
end
Have you looked into the paperclip gem? The gem offers direct compatibility to s3 and works great.

Django Custom File Storage - write contents to file

I'm trying to write a custom django backend that writes the contents of an uploaded file to an output file while also saving the file as it normally would. I assumed I could do this by overriding the _open function of Django, but no luck. Anyone know how to accomplish this? Here's what I've been messing around with
from django.core.files.storage import FileSystemStorage
class TestStore(FileSystemStorage):
def _open(self, name, mode='rb'):
data = open(name, 'rb')
dataRead = data.read()
filename = '/home/somewhere/testdir/output.txt'
FILE = open(filename, 'w')
FILE.write(dataRead)
FILE.close()
data.close()
return name
If you already created the file you can just output it with content-disposition:
response = HttpResponse(data, content_type='text/txt')
response['Content-Disposition'] = 'attachment; filename=filename'

Resources