How do I attach filenames with extensions in Rails 6 ActiveStorage? - ruby-on-rails

I am using ActiveStorage in Rails 6. I am clear with the concept of has_one_attached and has_many_attached.
From that I had few questions:
Is possible to upload original filename with extension to storage instead of key?
How to specify storage path during has_many_attached. i.e I have 5 files that need to stored under object specific folder.
e.g /path/to/images/<image_id>/

The key is a secure token that points from your application's blob to the right file stored on the service (S3, etc.). Although you can't use something instead of the key, it is entirely possible to store the original (or any other) file/path when you attach. For example, given an instance of a class that has_many_attachments :images:
message.images.attach(io: File.open('/wherever/thing1.png'),
filename: '/path/to/images/thing1.png',
content_type: 'image/png')
The filename and content_type are stored with the blob, and can be used when querying:
message.images.blobs.find_by(filename: '/path/to/images/thing1.png')
=> #<ActiveStorage::Blob id: 1, key: "...", filename: "/path/to/images/thing1.png" ...>
So, if you have five files under a specific folder you would simply open/upload them and specify the appropriate :filename when you attach.

Related

Storing files from Imgur in POSTGRES using Rails

I am trying to set up a database to store png files extracted from Imgur and I wanted to know if it would be possible to directly store my images into my database rather than just the link. I currently am storing image links from a txt file with selected Imgur links using activerecord in my seed.rb file as seen in the code below:
image = []
images_c = [:url]
x = 0
while x < 25 do
image_hash = {:url => url_file_lines[x]}
image << image_hash
x = x + 1
end
Image.import images_c, import, validate: false
I currently have a good idea of how to retrieve the files via the imgur link but am having a hard time figuring out how to convert the image file into a datatype that could be stored into the postgres db.
Store images in database (blob) is a bad idea. Just because you will get unnecessary growth of db. Instead of store in database - I recommend use some gems for storing files (shrine). If you has not any cloud storage - you can just save it in on your local storage. You just need add column image_data at your table with type jsonb (as you use postgres) and then mount uploader class into your model (like on gem documentation).

Active storage seed Rails

I want to seed my db with some instances containing active storage attachments, but i don't how i can do it. I tried some methods but not a success.
There is my Seed.
User.create(email: "test#ok.com", password: "okokok") if User.count.zero?
50.times do |i|
temp = Template.create(
title: Faker::Name.name,
description: Faker::Lorem.paragraph(2),
user: User.first,
github_link: Faker::SiliconValley.url,
category: rand(0...4)
)
puts Template.first.photo
temp.photo.attach(Template.first.photo)
end
Thx for your help
It's also in the documentation guide since a couple of days:
http://edgeguides.rubyonrails.org/active_storage_overview.html#attaching-file-io-objects
Sometimes you need to attach a file that doesn’t arrive via an HTTP
request. For example, you may want to attach a file you generated on
disk or downloaded from a user-submitted URL. You may also want to
attach a fixture file in a model test. To do that, provide a Hash
containing at least an open IO object and a filename:
#message.image.attach(io: File.open('/path/to/file'), filename: 'file.pdf')
When possible, provide a content type as well. Active Storage attempts
to determine a file’s content type from its data. It falls back to the
content type you provide if it can’t do that.
#message.image.attach(io: File.open('/path/to/file'), filename: 'file.pdf', content_type: 'application/pdf')
If you don’t provide a content type and Active Storage can’t determine
the file’s content type automatically, it defaults to
application/octet-stream.
Ok i found a solution, i post it for guys in the same situation:
temp.photo.attach(
io: File.open('storage/3n/GG/3nGGV5K5ucYZDYSYojV8mDcr'),
filename: 'file.png'
)
If you have more easiest solutions share it ;)

Stream uploading large files using aws-sdk

Is there a way to stream upload large files to S3 using aws-sdk?
I can't seem to figure it out but I'm assuming there's a way.
Thanks
Update
My memory failed me and I didn't read the quote mentioned in my initial answer correctly (see below), as revealed by the API documentation for (S3Object, ObjectVersion) write(data, options = {}) :
Writes data to the object in S3. This method will attempt to
intelligently choose between uploading in one request and using
#multipart_upload.
[...] You can pass :data or :file as the first argument or as options. [emphasis mine]
The data parameter is the one to be used for streaming, apparently:
:data (Object) — The data to upload. Valid values include:
[...] Any object responding to read and eof?; the object must support the following access methods:
read # all at once
read(length) until eof? # in chunks
If you specify data this way, you must also include the
:content_length option.
[...]
:content_length (Integer) — If provided, this option must match the
total number of bytes written to S3 during the operation. This option
is required if :data is an IO-like object without a size method.
[emphasis mine]
The resulting sample fragment might look like so accordingly:
# Upload a file.
key = File.basename(file_name)
s3.buckets[bucket_name].objects[key].write(:data => File.open(file_name),
:content_length => File.size(file_name))
puts "Uploading file #{file_name} to bucket #{bucket_name}."
Please note that I still haven't actually tested this, so beware ;)
Initial Answer
This is explained in Upload an Object Using the AWS SDK for Ruby:
Uploading Objects
Create an instance of the AWS::S3 class by providing your AWS credentials.
Use the AWS::S3::S3Object#write method which takes a data parameter and options hash which allow you to upload data from a file, or a stream. [emphasis mine]
The page contains a complete example as well, which uses a file rather than a stream though, the relevant fragment:
# Upload a file.
key = File.basename(file_name)
s3.buckets[bucket_name].objects[key].write(:file => file_name)
puts "Uploading file #{file_name} to bucket #{bucket_name}."
That should be easy to adjust to use a stream instead (if I recall correctly you might just need to replace the file_name parameter with open(file_name) - make sure to verify this though), e.g.:
# Upload a file.
key = File.basename(file_name)
s3.buckets[bucket_name].objects[key].write(:file => open(file_name))
puts "Uploading file #{file_name} to bucket #{bucket_name}."
I don't know how big the files you want to upload are, but for large files a 'pre-signed post' allows the user operating the browser to bypass your server and upload directly to S3. That may be what you need - to free up your server during an upload.

rails helper to give a upload file a unique name?

Hey guys
I'm now working on a project that require upload a lot of videos, Does rails have this helper can handle this, like the address of youtube video :
www.youtube.com/watch?v=KYUhtPV_Lk4
Thanks
You can generate a random string like this and use it as the file name:
Digest::SHA1.hexdigest(Time.now.to_s) # => 800b262b59296b660a4f73e23580809143ed8846
are you using activerecord to model the files or are they simply flat files somewhere?
if you have a model like UploadedFile << ActiveRecord::Base for each file you can just use the id of the model or if you want a string you can hash it with some string added as salt.
irb(main):021:0> file_id = 1
=> 1
irb(main):022:0> Digest::SHA1.hexdigest('SomeRandomString' + file_id.to_s)
=> "70f5eedc8d4f02fd8f5d4e09ca8925c2f8d6b942"
if you are simply keeping them as flat files on the system, you can hash their path+filename to create a unique string.
irb(main):016:0> Digest::SHA1.hexdigest '/home/bob/somefile.mp4'
=> "204a038eddff90637c529af7003e77d600428271"
and you can always add in a timestamp of the current time and a random number to prevent dupes.
SecureRandom.uuid generates a v4 random UUID (Universally Unique IDentifier)
It doesn't contain meaningful
information such as MAC address, time, etc.
See RFC 4122 for
details of UUID.
SecureRandom::uuid

File upload in Rails- data is an object, how do I return it in my view?

When doing an upload in my Rails project, the database stores
--- !ruby/object:File
content_type: application/octet-stream
original_path: my.numbers
how do I get it to return my.numbers in my view only?
Thanks a bunch!
Marco
ps. I don't want to use attachment_fu or any other plugin preferably.
A file upload is actually received by your controller as a File object, not as data, so it is your responsibility to read it in. Typically uploaded files are saved in a temporary directory and an open filehandle to it is present in the params.
You could do something like the following to retrieve the data:
def create
# Read in data from file into parameter before creating anything
if (params[:model] and params[:model][:file])
params[:model][:file] = params[:model][:file].read
end
#model = MyModel.create(params[:model])
end
You would probably need to be sure that the column in the database can store binary data. In MySQL migrations this is the :binary column type.
You can access the name of the uploaded file with the helper original_filename.
so params[:model][:file].original_filename

Resources