I am currently working on a Rails project where a file gets uploaded to Drive. I am able to get files uploaded to Drive however am wondering how to get a response that contains the file ID, link, etc. Do I need to use list for this? Any help would be greatly appreciated.
def create
#essay = Essay.new(params.require(:essay).permit(:course_name))
# Uploaded File
uploaded_io = params[:essay][:essay_draft]
# Save to a temporary folder
Tempfile.open(uploaded_io.original_filename, Rails.root.join('private', 'tmp')) do |f|
# Write using UTF-8 encoding
f.write(uploaded_io.read.force_encoding("UTF-8"))
# Close the file
f.close
# Gotta unlink to delete the temp file
f.unlink
end
# Set Metadata to be sent to Google Drive
file_metadata = {
name: uploaded_io.original_filename,
mime_type: 'application/vnd.google-apps.document'
}
# Call method which will upload the actual file to Drive
#drive.create_file(file_metadata,
fields: 'id',
upload_source: uploaded_io.path,
content_type: 'text/doc')
if #essay.save
redirect_to #essay
else
render :new
end
end
This is what I have in my create method.
def create
#essay = Essay.new(params.require(:essay).permit(:course_name))
# Uploaded File
uploaded_io = params[:essay][:essay_draft]
# Set Metadata to be sent to Google Drive
file_metadata = {
name: uploaded_io.original_filename,
mime_type: 'application/vnd.google-apps.document'
}
# Call method which will upload the actual file to Drive
#file = #drive.create_file(file_metadata,
fields: 'id, web_view_link',
upload_source: uploaded_io.path,
content_type: 'text/doc')
if #essay.save
render :show
else
render :new
end
end
I was then able to put the following into my view:
<%= #file.id %>
<%= #file.web_view_link %>
Related
I have a controller action that renders a pdf for download.
I want to render multiple pdfs to a tmp folder ( then zip them for download )
I can generate the pdfs and present to the user but I can't figure out how to create a folder to store them in.
I'm using prawn. It has the render_file method to save it to the filesystem but I don't know what directory it is or if other users could save their pdf's to the same folder, so I need to create a uniques folder for each user then save the pdf's there.
How can I do this?
my controller action is currently
def showpdf
respond_to do |format|
format.html
format.pdf do
#items.each do |pdf|
pdf = Prawn::Document.new(page_size: "A4",margin: [0,0,0,0])
# pdf creation stuff...
# this was used previously to render one pdf to the browser
# but I need to save multiple pdf's
#send_data pdf.render, filename: 'report.pdf', type: 'application/pdf'
end
end
end
You will need to store all files into tmp/your-folder folder, something like this
require 'prawn'
#items.each do |item|
pdf = Prawn::Document.new
pdf.text("Lets Zip All.")
pdf.render_file('tmp/your-folder/#{item.id}.pdf')
end
and then simply use https://github.com/rubyzip/rubyzip to zip the your-folder.
require 'zip'
folder = "tmp/your-folder/"
zipfile_name = "tmp/archive.zip"
input_filenames = Dir.entries("tmp/your-folder/").select {|f| !File.directory? f}
Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
input_filenames.each do |filename|
zipfile.add(filename, folder + '/' + filename)
end
zipfile.get_output_stream("myFile") { |os| os.write "myFile contains just this" }
end
Simply send the file to user. But if the PDF contains a lot of data move them to delayed jobs.
Hope this makes sense but if not please hit reply.
I am trying to send multiple files to the browser. I cant call send_data for every record like in the code below because i get a double render error. According to this post i need to create the files and zip them so i can send them in one request.
#records.each do |r|
ActiveRecord::Base.include_root_in_json = true
#json = r.to_json
a = ActiveSupport::MessageEncryptor.new(Rails.application.config.secret_token)
#json_encrypted = a.encrypt_and_sign(#json)
send_data #json_encrypted, :filename => "#{r.name}.json" }
end
I am creating an array of hashes with the #json_encrypted and the file_name for each record. My question is how can i create a file for every record and then bundle them into a zip file to then pass that zip file to send_file. Or better yet have multiple file download dialogs pop up on the screen. One for each file.
file_data = []
#records.each do |r|
ActiveRecord::Base.include_root_in_json = true
#json = r.to_json
a = ActiveSupport::MessageEncryptor.new(Rails.application.config.secret_token)
#json_encrypted = a.encrypt_and_sign(#json)
file_data << { json_encrypted: #json_encrypted, filename: "#{r.name}.json" }
end
So the issue i was having is that send_file does not respond to an ajax call which is how i was posting to that action. I got rid of the ajax, and am sending the necessary data though a hidden_field_tag and submitting it with jquery. The code below creates files for the data, zips them, and passes the zip file to send_data.
file_data.each do |hash|
hash.each do |key, value|
if key == :json_encrypted
#data = value
elsif key == :filename
#file_name = value
end
end
name = "#{Rails.root}/export_multiple/#{#file_name}"
File.open(name, "w+") {|f| f.write(#data)}
`zip -r export_selected "export_multiple/#{#file_name}"`
send_file "#{Rails.root}/export_selected.zip", type: "application/zip", disposition: 'attachment'
end
I'm developping a web application in rails 4 and I'm currenty faced with a tiny issue.
I want to make the users of the website to be able to download files from a ftp by clicking on a link. I've decided to go on this:
def download
#item=Item.find(params[:id])
#item.dl_count += 1
#item.save
url = #item.file_url.to_s
redirect_to url and return
end
And, very basically, this in my view:
<%= link_to 'DL', controller: "items", action: "download"%>
However, I'm not quite satisfied by this, as it generates a few mistake like the fact that clicking the link create two GET methods, one responding by 403 Forbidden and the next with a 302 found...
Do you have any idea about how I could improve this?
In Rails you should do:
def download
#item=Item.find(params[:id])
#item.dl_count += 1
#item.save
url = #item.file_url.to_s
send_file url, type: 'image/jpeg', disposition: 'inline'
end
Take a look for more information http://apidock.com/rails/ActionController/DataStreaming/send_file
Note that send_file can send only from local file system.
If you need get file from remote source (should be secure location) like http://example.com/apps/uploads/tfm.zip and avoid store this file in server memory, you can first save file in #{RAILS_ROOT}/tmp/ or system /tmp and then send_file
data = open(url)
filename = "#{RAILS_ROOT}/tmp/my_temp_file"
File.open(filename, 'w') do |f|
f.write data.read
end
send_file filename, ...options...
If Rails can`t read file, you should check file permission
I am using this file uploader example for Ruby on Rail.
Now the files are saved by their IDs into: public->system->uploads->uploads->000->000.
I need it to be saved into such system of folders: public->system->Files->Types(JPG OR PDF)
The files have to be saved not according to their IDs but according to their types.
Example:
All jpg files should be saved into:
public->system->Files->JPG
All PDF files should be saved into:
public->system->Files->PDF
I have already created the folders, I need only to specify a path where the files have to be saved.
That is a code from uploads_controller.rb
def create
p_attr=params[:upload]
p_attr[:upload] = params[:upload][:upload].first if params[:upload][:upload].class == Array
#upload = Upload.new(p_attr)
respond_to do |format|
if #upload.save
format.html {
render :json => [#upload.to_jq_upload].to_json,
:content_type => 'text/html',
:layout => false
}
format.json { render json: [#upload.to_jq_upload].to_json, status: :created, location: #upload }
else
format.html { render action: "new" }
format.json{ render json: {name:(#upload.upload_file_name).split(".").first ,error: #upload.errors.messages[:upload_file_name]}, :status =>422}
end
end
end
Thanks in advance
I'd recommend switching to carrierwave uploader. There are config options , which are going to help you in separating files by type . I mean , in this configuration in carrierwave uploader :
def store_dir
"uploads/#{Rails.env}/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
you can alter the 'model.id' part with a method , naming the image by type .
You can manually do this using regex. So it'd be something like:
type = p_attr.filename.match(/^*\w{3,}$/).to_s
filename being whatever its called within p_attr. So that would give you the file type ending as a string. According to the paperclip gem you can set a different path and url by setting them as a param:
path = "#{Rails.env}/public/system/FILE/#{type}"
#upload = Upload.new(p.attrs, path: path, url: path)
Not 100% sure on the upload line, but something like that should be able to override the paperclip default.
For more information check the paperclip readme section about storage. It explains about overriding default path.
Changing Paperclip File Storage Location
I'm working on my first application and I need some help with allowing my users to download a text file with certain variables that are being displayed on the page.
Take a shopping list for example.
Let's say you allow your users to create a shopping list of products, and then display the shopping list with the items on a shopping list page,
e.g. localhost:3000/list/my-list
Take a look at the example code below (which is probably incorrect):
File.open('shopping_list.txt', 'w') do |file|
file.puts 'Item 1: #{product_1.name}'
file.puts 'Item 2: #{product_2.name}'
file.puts 'Item 3: #{product_3.name}'
end
Which then creates a text file that has the following content:
Item 1: Eggs
Item 2: Butter
Item 3: Bread
Users should then be able to download this file (i don't want this file to be stored on the server) via a download link.
I have no idea how to achieve this, but I'm hoping you guys can guide me. :D
TL;DR
create text files populated with model data (perhaps create a method to achieve this?)
text files should not be stored on the server, but created as users click the download button (not sure if this is the rails way but perhaps someone could show me a better way)
I am assuming there is a resource for List with the attribute name as the name of the list and a list has_many Item which has an attribute description
First off, create a download path change your routes config/routes.rb
resources :lists do
member {get "download"}
end
Now if you run a rake routes in the console you should see a route like
/lists/:id/download
Whats more you should now have the helpers download_list_url & download_list_path to use in your view like
<ul>
<% #lists.each do |list| %>
<li> <%= list.name %> - <%= link_to 'Download List', download_list_path(list) %> </li>
<% end %>
</ul>
In your lists_controller add the action, and as you dont actually want to keep the file on the server disk just stream the data as a string
def download
list = List.find(params[:id])
send_data list.as_file,
:filename => "#{list.name}.txt",
:type => "text/plain"
end
Finally you see I have used a as_file method which you should add to the model (I prefer not to do this stuff in controllers, fat models, skinny controllers). So in the List model
def as_file
output = [self.name]
self.items.each {|item| output << item.description }
output.join("\n")
end
You say you don't want to store the file on the server, but "download" it on request; this sounds like you just want to generate and deliver a text document in response to the download link. There are several approaches, but you want to be sure of setting the mime-type so the browser sees it as a text file instead of an html document.
product_info = [
"Item 1: #{product_1.name}",
"Item 2: #{product_2.name}",
"Item 3: #{product_3.name}",
].join("\n")
render :text => product_info # implies :content_type => Mime::Type["text/plain"]
BTW, your example with open/puts above won't output what you think since single-quoted strings don't interpolate.
so, you wish to :
create text files populated with model data (perhaps create a method
to achieve this?)
text files should not be stored on the server, but
created as users click the download button (not sure if this is the
rails way but perhaps someone could show me a better way)
You have the right idea, here's what to do :
Create a method in your model to generate the text file contents. Let's say this method is called list_data
It seems like you have an existing controller action called my_list. Hence we can call our new method in the controller like so :
.
def my_list
# pre-existing code
respond_to do |format|
format.html # show html page as before
format.text do
send_data #list.list_data, :content_type => 'text/plain', :filename => 'my-shopping-list.txt'
end
end
end
To link to the download, just use link_to :action => my_list, :format => 'text'
See http://api.rubyonrails.org/classes/ActionController/DataStreaming.html#method-i-send_data for full docs on send_data
Caveat & explanations : Using the method above, there isn't really an explicit creation of files, Rails is streaming it for you. Hence this method is not suitable for very large files, or when the generation of the file content will take a while. Use a delayed method to generate the file and store it - the file contents somewhere if that's the case - but we can use send_data once it has been generated
You could try a combination of TempFile and send_file. In your controller action ..
file = Tempfile.new('foo')
file.write("hello world")
file.close
send_file file.path
At Rails 2.3 you can use Template Streaming. Working with Redmine I can remember something like that, you have to adapt for your case. Reference: Streaming and file downloads
require "prawn"
class ClientsController < ApplicationController
# Generate a PDF document with information on the client and return it.
# The user will get the PDF as a file download.
def download_pdf
client = Client.find(params[:id])
send_data(generate_pdf, :filename => "#{client.name}.pdf", :type => "application/pdf")
end
private
def generate_pdf(client)
Prawn::Document.new do
text client.name, :align => :center
text "Address: #{client.address}"
text "Email: #{client.email}"
end.render
end
end
Using the Thong Kuah you must just change the "content_type" param:
def my_list
# pre-existing code
respond_to do |format|
format.html # show html page as before
format.text do
send_data #list.list_data, :content_type => 'text/plain', :filename => 'my-shopping-list.txt'
end
end
end