'Rails - how to access ActionDispatch UploadedFile default attributes in my methods?

I'm new to rails and building a simple bank statement converter app. User would select bank name, upload their statement, click "convert", and the converted file will be automatically downloaded.

How to access Rails ActionDispatch::::Http::UploadedFile attributes, in particular the content_type attribute, in the controller? I would like to check for correct file type before passing the file for futher processing in the models by using that attribute in my validation methods.

I tried .content_type and [:content_type], none worked, resulting in NoMethodError even though the method is clearly defined as private method in the controller.

Here's my current application setup:

  • in the webpage, a form would allow user to submit :bank and :statement params.

  • in ConvertersController, the params are secured using the private converter_params method, resulting in the following full params (as captured in server log):

Parameters: {"authenticity_token"=>"[FILTERED]", "converter"=>{"bank"=>"bank1", "statement"=>#<ActionDispatch::Http::UploadedFile:0x000055d9699140e0 @tempfile=#<Tempfile:/tmp/RackMultipart20210401-31472-dvsdde.pdf>, @original_filename="bank1.pdf", @content_type="application/pdf", @headers="Content-Disposition: form-data; name=\"converter[statement]\"; filename=\"bank1.pdf\"\r\nContent-Type: application/pdf\r\n">}, "commit"=>"convert"}

  • Then in :create action, I applied file_check private method onto the uploaded statement (i.e. converter_params[:statement]) to check whether the file type is correct:
ConvertersController < ApplicationController
.
.
.
  def create
    @converter  = Converter.new(converter_params)

    if converter_params[:statement].nil?
      flash.now[:alert] = "Error: no statement provided"
      render action: "index"
    elsif converter_params[:statement].file_check
      flash.now[:alert] = "Error: wrong file type"
      render action: "index"
    else
      result      = @converter.run_conversion
      file_path   = Rails.root.join('public', 'output', result)

      stream_then_delete_statement(file_path)
    end
  end
.
.
.

  private 

  def converter_params
    params.require(:converter).permit(:bank, :statement)
  end

  def file_check
    pdf_statement = [ "bank1" ]
    csv_statement = [ "bank2" ]

    if pdf_statement.include? converter_params[:bank]
      self.content_type.pdf_check?
    elsif csv_statement.include? converter_params[:bank]
      self.content_type.csv_check?
    else
      exit(0)
    end
  end
.
.
.
end
  • then in the model, there are csv_check? and pdf_check? methods that the file_check private method is referring to:
class MyModel < ActiveRecord::Base

  def pdf_check?
    self == "application/pdf"
  end

  def csv_check?
    self == "application/csv"
  end

.
.
.

end

However, currently I got NoMethodError, even though the file_check method is clearly defined as private method at the ConvertersController. This error baffles me, as other private methods such as stream_then_delete_statement is working normally.

full error:

NoMethodError in ConvertersController#create

undefined method `file_check' for #<ActionDispatch::Http::UploadedFile:0x000055d9699140e0 @tempfile=#<Tempfile:/tmp/RackMultipart20210401-31472-dvsdde.pdf>, @original_filename="bank1.pdf", @content_type="application/pdf", @headers="Content-Disposition: form-data; name=\"converter[statement]\"; filename=\"bank1.pdf\"\r\nContent-Type: application/pdf\r\n">


Solution 1:[1]

You have defined the method file_check in class ConvertersController but calling it using an instance of class ActionDispatch::Http::UploadedFile.

Your error message says:

undefined method `file_check' for #<ActionDispatch::Http::UploadedFile:0x000055d9699140e0 @tempfile=#<Tempfile:/tmp/RackMultipart20210401-31472-dvsdde.pdf>, @original_filename="bank1.pdf", @content_type="application/pdf", @headers="Content-Disposition: form-data; name=\"converter[statement]\"; filename=\"bank1.pdf\"\r\nContent-Type: application/pdf\r\n">

This means its trying to call the method file_check on an object of class ActionDispatch::Http::UploadedFile but couldn't find one.

You should call the method directly and pass the statement params as an argument to the method:

elsif file_check(converter_params[:statement])
.
.
.
def file_check(statement_params)
.
.
.
end

You can find the current object by calling self. Try using a debugger like byebug https://github.com/deivid-rodriguez/byebug to check it.

Solution 2:[2]

The initialize method for ActionDispatch::Http::UploadedFile is expecting hash[:type] and not hash[:content_type].

The ruby documentation is not clear on this point.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Ro55