Ruby on Rails Paper clip old files name issue - ruby-on-rails

I'm using Rails 4 and after added paper clip initializer code to initialize the user files name by user ID to give every file different name using this code
In the initialize file
# initializers images names by the model id (:id)
Paperclip.interpolates :parent_id do |a, s|
a.instance.contract.id
end
In the model
has_attached_file :photo ,
:url => "/system/files/users/:basename_:id.:extension",
:path => ":rails_root/public/system/files/users/:basename_:id.:extension"
Now it's work well by give the file his basename_fileID.file extension
but I notes that I was missed all the old files links because the new link will search about the file contain the basename and fileID something like that "image_1.jpeg" but the old link search about basename only something like "image.jpeg"
so how I can solve it in Rails without change my old files name on server ??
Is there's some thing in Rails 4 allow me to inform the links to get the old path in some cases or not ??

Finally I solved this issue by check if the file exist on server or not by using File.exist?
The code
<% if File.exist?(#user.photo.path) %>
<!--The new path -->
<%= link_to "Photo", #user.photo.url %>
<% else %>
<!--The old path -->
<%= link_to root_url+'system/files/users/'+#user.photo_file_name, :title => "show image" do %>Photo<% end %>
<% end %>

Related

Handle raise ActiveStorage::InvariableError after Deleting a file that uploaded with active storage

Hello i uploaded an image with active storage specificly an avatar for my model users, but after of upload this image, i delete this of the folder that contained the file. then, i have one attachment that exists without file.
i am checking user.avatar.blob (the user with the deleted file), and then this return null (with the console), i am try to handle this inside of a view but dont works, any advice for handle this exception?
<% unless !!user.avatar.blob.nil? || user.avatar.attached? %>
#this a simple avatar api that show for default
# an image when dont exist attachement or the file dont works or dont exist
<%= image_tag "https://avatars.dicebear.com/4.5/api/#{user.gender}/#{user.username}.svg?mood[]=happy&mood[]=happy", :size => "100x100" %>
<% else %>
<%= image_tag user.avatar.variant(
resize_to_limit: [100, 100]
)%>
<% end %>

Get path to ActiveStorage file on disk

I need to get the path to the file on disk which is using ActiveStorage. The file is stored locally.
When I was using paperclip, I used the path method on the attachment which returned the full path.
Example:
user.avatar.path
While looking at the Active Storage Docs, it looked like rails_blob_path would do the trick. After looking at what it returned though, it does not provide the path to the document. Thus, it returns this error:
No such file or directory # rb_sysopen -
Background
I need the path to the document because I am using the combine_pdf gem in order to combine multiple pdfs into a single pdf.
For the paperclip implementation, I iterated through the full_paths of the selected pdf attachments and load them into the combined pdf:
attachment_paths.each {|att_path| report << CombinePDF.load(att_path)}
Use:
ActiveStorage::Blob.service.path_for(user.avatar.key)
You can do something like this on your model:
class User < ApplicationRecord
has_one_attached :avatar
def avatar_on_disk
ActiveStorage::Blob.service.path_for(avatar.key)
end
end
I'm not sure why all the other answers use send(:url_for, key). I'm using Rails 5.2.2 and path_for is a public method, therefore, it's way better to avoid send, or simply call path_for:
class User < ApplicationRecord
has_one_attached :avatar
def avatar_path
ActiveStorage::Blob.service.path_for(avatar.key)
end
end
Worth noting that in the view you can do things like this:
<p>
<%= image_tag url_for(#user.avatar) %>
<br>
<%= link_to 'View', polymorphic_url(#user.avatar) %>
<br>
Stored at <%= #user.image_path %>
<br>
<%= link_to 'Download', rails_blob_path(#user.avatar, disposition: :attachment) %>
<br>
<%= f.file_field :avatar %>
</p>
Thanks to the help of #muistooshort in the comments, after looking at the Active Storage Code, this works:
active_storage_disk_service = ActiveStorage::Service::DiskService.new(root: Rails.root.to_s + '/storage/')
active_storage_disk_service.send(:path_for, user.avatar.blob.key)
# => returns full path to the document stored locally on disk
This solution feels a bit hacky to me. I'd love to hear of other solutions. This does work for me though.
You can download the attachment to a local dir and then process it.
Supposing you have in your model:
has_one_attached :pdf_attachment
You can define:
def process_attachment
# Download the attached file in temp dir
pdf_attachment_path = "#{Dir.tmpdir}/#{pdf_attachment.filename}"
File.open(pdf_attachment_path, 'wb') do |file|
file.write(pdf_attachment.download)
end
# process the downloaded file
# ...
end

Uploading a File on aWebsite using Ruby Rails 4

I am using rails 4 and I have this code.
I created a controller and called it upload (upload_controller.rb)
I put this code in it:
class UploadController < ApplicationController
def index
render :file => 'app\views\upload\uploadfile.rhtml.erb'
end
def uploadFile
post = DataFile.save(params[:upload])
render :text => "File has been uploaded successfully"
end
end
My model was called data_file.rb. The code is the following:
class DataFile < ActiveRecord::Base
attr_accessor :upload
def self.save(upload)
name = upload['datafile'].original_filename
directory = "public/data"
# create the file path
path = File.join(directory, name)
# write the file
File.open(path, "wb") { |f| f.write(upload['datafile'].read) }
end
end
My view file was called uploadFile.html.erb. My code is the following:
<h1>File Upload</h1>
<%= form_tag({:action => 'uploadFile'}, :multipart => true) do %>
<p><label for="upload_file">Select File</label>
<%= file_field 'upload', 'datafile' %></p>
<%= submit_tag "Upload" %>
<% end %>
My main goal of having this code is so that the user can upload a file to a specified location. The file type has to be anything not just images but doc, excel sheets, etc. Once I write this code and I do bundle install and then I do rake db:migrate and I go to tools and run the development. Once I do that I go to firefox and type in localhost:3000 and the page cannot show. (directs me to yahoo search or whatever).
I don't know what I am doing wrong here. Am I suppose to add a gem or change a certain line or something? I have been stuck on this issue for days now and I just want to move on to the next part of my webpage. Please help me. Thank you.

Accessing Redmine controllers through a plugin

I've already asked the question on the Redmine official website but I didn't get any answer, maybe I'll have more chance here.
I'm working on a project and I try to improve an existing plugin for Redmine by adding some features to it to allow the user to upload his Dropbox files in the Redmine documents with a simple form. Redmine already has this possibilty so I would like to use the controller and methods already defined in the Redmine source code.
I have the following code in one of my plugin views:
<% html_title "Reddrop - Sync" %>
<h2>Synchronisation page</h2>
<p>Please choose your file</p>
<%= form_tag({:controller => "documents", :action => "add_attachment", :id => #document}, :multipart => true) do %>
<%= file_field_tag 'attachments[dummy][file]', :id => nil, :multiple => true, :onchange => "addInputFiles(this)" %>
<%= submit_tag(value = "Sync this file with Redmine") %>
<% end %>
I'm calling the "documents" controller and the add_attachment method which are defined in the Redmine source code. When I submit my form I get the following error:
ActionController::RoutingError (No route matches {:controller=>"documents", :action=>"add_attachment", :id=>nil}):
Is it possible to call these controllers/methods through a plugin if they are defined in the Redmine source code?
If yes, maybe could you give some advice to how configure my routes?
See here :id => #document
#document is the variable that contains nil however this should contain some id (like 1,2 or whatever) of required record check it in your controller and once you will fix this issue sure this error will be resolved.
validate route format
rake | rails route
send the expected parameters and the correct namespace. If you already have a #document you could use the form_for.
`<% form_for(#document, url: {action:'add_attachment'} ) do %> `

file upload without activerecord

How do you handle file upload in rail without attaching them to active record ?
I just want to write the files to the disk.
Thanks,
If I understand correctly what you need then the most simple example would be this:
The controller:
class UploadController < ApplicationController
def new
end
def create
name = params[:upload][:file].original_filename
path = File.join("public", "images", "upload", name)
File.open(path, "wb") { |f| f.write(params[:upload][:file].read) }
flash[:notice] = "File uploaded"
redirect_to "/upload/new"
end
end
The view:
<% flash.each do |key, msg| %>
<%= content_tag :div, msg, :class => [key, " message"], :id => "notice_#{key}" %>
<% end %>
<% form_tag '/upload/create', { :multipart => true } do %>
<p>
<%= file_field_tag 'upload[file]' %>
</p>
<p>
<%= submit_tag "Upload" %>
</p>
<% end %>
This would let you upload any file without any checks or validations which in my opinion isn't that usefull.
If I would do it myself then I would use something like validatable gem or tableless gem just tableless is not supported anymore. These gems would allow you to validate what you're uploading to make it more sane.
You can just move the temporary file to destiny path using FileUtils
tmp = params[:my_file_field].tempfile
destiny_file = File.join('public', 'uploads', params[:my_file_field].original_filename)
FileUtils.move tmp.path, destiny_file
The Tempfile documentation shows an example that's equivalent to Rytis's code, which is fine most of the time. But when you call tempfile.read, Ruby is reading the whole file as a single chunk into memory, which is sub-optimal.
However, FileUtils provides a copy_stream method, and IO, at least in Ruby 2.0, provides a copy_stream implementation that handles writing directly to a filepath (FileUtils.copy_stream requires File-like objects on both sides, or so say the docs).
In my case, I was initiating a large multi-file upload via AJAX, and wanted to avoid reading the whole file(s) into Ruby's memory before writing to disk.
In the example below, params[:files] is an Array of ActionDispatch::Http::UploadedFile instances, and local_filepath is a string pointing to a non-existing file in an existing directory. For brevity, I'll assume I'm only uploading one file:
IO.copy_stream(params[:files][0].tempfile, local_filepath)
The ActionDispatch::Http::UploadedFile instance has a .tempfile field that's just a regular Tempfile instance.
I'm not actually sure that Ruby still isn't reading the whole file into memory—I didn't benchmark anything—but it's a lot more possible than it is with the localfile.write(tempfile.read) syntax.
tl;dr: IO.copy_stream(your_tempfile, your_disk_filepath) is more concise, if not faster.
You could try using the Rails plugin Attachment_fu to handle file uploads. It allows you to save uploads to the file system instead of the database.

Resources