quicklikerabbit
quicklikerabbit

Reputation: 3556

Get filename in Rails backend from file upload in React frontend

I have a file upload component in React that makes a POST to my Rails API:

class uploadFormState {
  @observable file;

  constructor() {
     this.file = '';
  }

  @action.bound setFile(file){
   this.file = file[0];
   this.fileName = file[0].name;
  }

  @action.bound uploadFile() {
    $.ajax({
        url: AppConstants.APIEndpoints.USERLOCATIONS + '/1/photos/1/upload',
        headers: { "Authorization": localStorage.getItem('authToken') },
        data: this.file,
        processData: false,
        contentType: false,
        type: 'POST',
        success: function (data) {
            alert(this.data.name + " uploaded successfully!");
        }
     });
   }
}

export default new uploadFormState();

As you can see in the data field, I'm trying to pass in the original file name to the rails backend. The problem is, I don't know how to get these data params.

My upload method looks like this:

  def upload
    @photo.upload_model(request.body)
  end

And the class method `upload_model' looks like this:

  def upload_model(model_file)
    model = StorageBucket.files.new(
    key: "models/#{id}",
    body: model_file.read,
    public: true
    )

    model.save

    update_columns model_url: model.public_url
  end

I've tried changing the data field in the ajax call to data: {file: this.file, fileName: this.file.name} but I'm not sure how to access these two params from the rails side. It results it uploading a text of object Object if I change the data params this way. request.body is a StringIO ruby type. The method itself works when the data field is this.file (i.e. it uploads the file to google storage), however, the filename loses the extension, which isn't ideal.

So to sum up, how do I access the uploaded filename in the Rails backend?

Upvotes: 0

Views: 1055

Answers (2)

quicklikerabbit
quicklikerabbit

Reputation: 3556

Big thanks to @kasperite for suggesting the use of FormData, which was a big part of this answer. I changed the ajax call to look like this:

@action.bound uploadFile() {
  let form = new FormData();
  form.append("file", this.file);
  $.ajax({
    url: AppConstants.APIEndpoints.USERLOCATIONS + '/1/photos/1/upload',
    headers: { "Authorization": localStorage.getItem('authToken') },
    data: form,
    processData: false,
    contentType: false,
    type: 'POST',
    success: function (data) {
      alert(this.data.name + " uploaded successfully!");
    }
  });
}

And then changed my controller to look like this:

def upload
  @photo.upload_model(params[:file].original_filename, params[:file].tempfile)
end

params[:file].original_filename is the filename while params[:file].tempfile is the actual file.

And my class method now looks like this:

def upload_model(original_filename, model_file)
  model = StorageBucket.files.new(
  key: "models/#{id}-#{original_filename}",
  body: model_file.read,
  public: true
  )

  model.save

  update_columns model_url: model.public_url
end

Upvotes: 0

kasperite
kasperite

Reputation: 2478

Have you tried using FormData?In your case, it will be:

JS:

// Use FormData to build params 

let form = new FormData();
form.append("file_name", file[0].name);

// Send form as part of ajax post

$.ajax({
  ...
  data: form,
  ...
})

Then on the Rails side, you can get file_name through params[:file_name] (in controller)

That's a quick suggestion, hopefully it will help you.

Cheers

Upvotes: 1

Related Questions