Search code examples
ruby-on-railscontrollerrails-activerecordrails-activestorage

How to pass selected filename to controller to process in Rails


I want to select a zip file on local computer and process it before adding to database.

I've got the controller working to do all of this except pass in the filename. It works if I put in the filename manually.

<%= form_with model: @bp, url: { action: "import_data" } do |form| %>
  <%= form.file_field :health_exported_zip %>
  <%= submit_tag( "Import" ) %>
<% end %>

bp_controller

def import_data
  # Following line errors
  health_exported_zip_file = params[:health_exported_zip] # no implicit conversion of nil into String
  # Manually setting file location which works with the above line commented out
  health_exported_zip_file = "/Users/username/Downloads/export.zip"
  unzip_import_data(health_exported_zip_file)
  …
end

def unzip_import_data(health_exported_zip_file)
  require 'zip'
  unzip_to =  "import_staging"
  …
end

# Only allow a list of trusted parameters through.
def bp_params
  params.require(:bp).permit(:statdate, :systolic, :diastolic, :heartrate, :sourceName, :sourceVersion, :comment, :health_exported_zip)
end

This must be simple to do, but I'm at a loss. ActiveStorage using similar form_with must have access to the file when file uploading. Thank you

So not simple, but @masasakano pointed me to reading params which I parsed as follows to get path to the temporary copy of the zip file.

indexTempfile = params.to_s.index('/var/folders') # setting start of Tempfile location
tempfile_path = params.to_s.slice!(indexTempfile..450) # slice off the beginning of params
indexEnd = tempfile_path.to_s.index('>') - 1 # get end of Tempfile location. 
tempfile_path = tempfile_path.to_s.slice!(0..indexEnd) # slice off the end of params and left with path

tempfile_path is what need as health_exported_zip_file. Renamed since it's not the original file.


Solution

  • What params returns is an ActionDispatch::Http::UploadedFile object (see Official reference). So the returned value of params is not the filename as you manually set with unzip_import_data(health_exported_zip_file).

    Instead you can use params[:bp][:health_exported_zip].path (which is an alias of params[:bp][:health_exported_zip].tempfile.path) to get the path of the uploaded file, which you can feed to your unzip function.

    So, that is a problem I have noticed.

    However, if the exception you encounter is no implicit conversion of nil into String as you state, then I guess params has failed to pick up the uploaded file in the first place and then params[:bp][:health_exported_zip] returns nil. There are numerous potential causes for it, such as, you actually didn't uploaded a file in your attempt (even though you think you did), or the uploaded file exceeded the maximum permitted size, or the controller is not called from the view as you intend, etc…

    My advice is to dump params in your particular case to see what is passed to your controller as a starting point of your investigation.

    [Edit]
    After exchanging comments, it turned out the pre-filtering method the OP included in the Controller is not called in the OP's test case. Then, health_exported_zip is filtered out from params because of Rail's strong parameter constraint, and params[:bp][:health_exported_zip] would return nil. So, you should add :import_data in the only clause in your before_action to call the pre-handing method. Alternatively, a more direct way is to add the line of your chosen params.permit etc at the head of the import_data method.