Reputation: 123
I am trying to import data from a CSV file, I don't want to save the file. I have found a similar question here: Ruby on Rails - Import Data from a CSV file
However, when I try and use this solution I get an error.
My Form is as follows:
Import a CSV file
<%= file_field_tag :file %>
<div class="actions form-group" id="button_text">
<p>
<%= button_tag(type: 'submit', class: "btn btn-primary button-submit") do %>
Submit
<% end %>
On the new page the submission of this form calls the create action, the create action then calls the line:
Product.import(params[:file])
I have the following import method in my model:
def self.import(myfile)
require 'csv'
csv_text = File.read(myfile)
csv = CSV.parse(csv_text, :headers => true)
csv.each do |row|
end
end
where 'myfile' is params[:file].
I am aware I am not doing anything with the data yet. I am getting an error with line csv_text = File.read(myfile)
which is: No such file or directory @ rb_sysopen - X.csv
When looking for a solution I have seen .path is used. If i change this line to csv_text = File.read(myfile.path)
I get the error: undefined method `path' for "X.csv":String
Can anybody help? I feel like I must be very close to a solution but I just keep going round and round between these two errors. Is it possible to get the data from the csv without saving it first?
Thanks for your time
Upvotes: 0
Views: 2429
Reputation: 12592
The most important thing to remember with file uploads is that the rendered form's encoding MUST be set to multipart/form-data
. I guess you have that maintained.
<%= form_tag({action: :upload}, multipart: true) do %>
<%= file_field_tag 'file' %>
<% end %>
Now in controller you will have access to IO
object via params
. The object in the params hash is an instance of a subclass of IO.
The object will have an original_filename
attribute containing the name the file had on the user's computer and a content_type
attribute containing the MIME
type of the uploaded file.
You can either move the temporary file to some dir in your public
directory or simple use and throw away.
def upload
uploaded_io = params[:file]
# Moving the file to some safe place; as tmp files will be flushed timely
File.open(Rails.root.join('public', 'uploads', uploaded_io.original_filename), 'wb') do |file|
file.write(uploaded_io.read)
end
end
for you, you can leverage the tempfile
attribute in the IO
object. The actual file is accessible via the tempfile
accessor, though some of its interface is available directly for convenience.
So, this should work
...
csv_text = File.read(file.tempfile)
...
> uploaded_io = params[:file]
=> #<ActionDispatch::Http::UploadedFile:0x007f4154fe9a38
@content_type="application/pdf",
@headers="Content-Disposition: form-data; name=\"file\"; filename=\"3423loadshedding.pdf\"\r\nContent-Type: application/pdf\r\n",
@original_filename="3423loadshedding.pdf",
@tempfile=#<File:/tmp/RackMultipart20160421-14883-1lv0qtq.pdf>>
[2] pry(#<PostsController>)> uploaded_io.tempfile
=> #<File:/tmp/RackMultipart20160421-14883-1lv0qtq.pdf>
> uploaded_io.tempfile.class
=> Tempfile
For Error
ProductsController#upload No such file or directory @ rb_sysopen - /<path to my app>/public/uploads/X.csv
You need to first create the folder public/uploads
$ mkdir public/uploads
Now try; the file should be moved. This is how Paperclip
and Carrierwave
manage uploaded files.
Upvotes: 1
Reputation: 700
you can do something like this way .html.erb
<%= form_for(<object>, url: <path>, method: :post, remote: :true, html: {
multipart: true, }) do |f| %>
<input type="file" name="file_batch_upload" accept=".csv />
<% end %>
//Define that function
def self.import(file)
require 'csv'
csv_text = File.read(file.tempfile)
csv = CSV.parse(csv_text, :headers => true)
csv.each do |row|
//row["name"]
end
end
Upvotes: 2