Paperclip image upload (base64) - getting 500 internal server error (undefined method 'permit')

1.5k views Asked by At

I'm trying to upload an image using Paperclip; I followed the paperclip quick start guide as closely as possible, but I'm doing something wrong and can't find out what. I'm new to Ruby and Rails; the whole project is set up with the AngularJS framework.

What I'm doing is sending a POST request that contains a base64 encoded image:

DataService.add("uploads", {image: file}) [...]

The service looks like this:

add: function(type, object) {
        return $http.post(API_URL + "/" + type, object) [...]

"file" is the base64 encoded image; I hope it is somewhat clear how the POST works. Now the backend:

Controller:

module Api
class UploadsController < ApplicationController

    # POST api/uploads
    def create
        p params
        @upload = Upload.create(upload_params)
        @upload.save
    end

    private

        def upload_params
            params.require(:upload).permit(:image)
        end
end
end

Model:

class Upload < ActiveRecord::Base
attr_accessor :content_type, :original_filename, :image_data
before_save :decode_base64_image

has_attached_file :image, :styles => { :medium => "300x300>", :thumb => "100x100>" }, :default_url => "/storage/images/:style/missing.png", :url => "/:class/:attachment/:id/:style_:basename.:extension"

validates_attachment_content_type :image, :content_type => /\Aimage\/.*\Z/
validates :image, :attachment_presence => true
# validates_attachment :image, :size => { :in => 0..100.kilobytes }

protected

    def decode_base64_image
        if image_data && content_type && original_filename
            decoded_data  = Base64.decode64(image_data)

            data = StringIO.new(decoded_data)
            data.class_eval do
                attr_accessor :content_type, :original_filename
            end

            data.content_type = content_type
            data.original_filename = File.basename(original_filename)

            self.image = data
        end
    end
end

That's basically it - I hope I included everything that's important for my problem (first question here).

When testing this with a simple image I get a 500 - Internal server error, with request payload

{"image":"data:image/png;base64,...<base64 code>...}

I understand that the mistake is most likely in the controller/model part, but I don't know how to solve this.

Thanks for your help :)


EDIT:

When testing this locally with Postman (form-data, POST, Key: upload and an image) I get this:

Started POST "/api/uploads" for 127.0.0.1 at 2015-06-13 16:34:08 +0200
Processing by Api::UploadsController#create as */*
Parameters: {"upload"=>#<ActionDispatch::Http::UploadedFile:0x00000002f0e108 @tempfile=#     <Tempfile:/tmp/RackMultipart20150613-7296-8zu01e.jpeg>,     @original_filename="meise.jpeg", @content_type="image/jpeg",    @headers="Content-Disposition: form-data; name=\"upload\"; filename=\"meise.jpeg\"\r\nContent-Type: image/jpeg\r\n">}
{"upload"=>#<ActionDispatch::Http::UploadedFile:0x00000002f0e108 @tempfile=#<Tempfile:/tmp/RackMultipart20150613-7296-8zu01e.jpeg>, @original_filename="meise.jpeg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"upload\"; filename=\"meise.jpeg\"\r\nContent-Type: image/jpeg\r\n">, "controller"=>"api/uploads", "action"=>"create"}
Completed 500 Internal Server Error in 0ms

NoMethodError (undefined method `permit' for #    <ActionDispatch::Http::UploadedFile:0x00000002f0e108>):
app/controllers/api/uploads_controller.rb:16:in `upload_params'
app/controllers/api/uploads_controller.rb:7:in `create'

EDIT 2:

It works now, but for everyone with the same problem: if you're sending the base64 encoded image like I'm doing it in the DataService and having the same controller/module setup, it will work with Paperclip, but not with the frontend. In

has_attached_file :image, :styles => { :medium => "1000x1000>", :thumb => "150x150#" }, :default_url => "/storage/images/:style/missing.png", :url => "/:class/:attachment/:id/:style_:basename.:extension"

I had to change the last part to

"/:class/:attachment/:id/:style_image_:id.png"

Because somehow the name and extension got lost somewhere. I used the id as the file name now, and .png as an extension because the base64 is created from a png that I created in the frontend.

1

There are 1 answers

3
Nitish Parkar On BEST ANSWER

The problem is with this line:

params.require(:upload).permit(:image)

require ensures that a parameter is present. If it's present, returns the parameter at the given key. In your case it is returning ActionDispatch::Http::UploadedFile object which doesn't have permit method.

You can solve this by removing require and passing the file in image parameter instead of upload:

params.permit(:image)

If you want to keep the existing code, have the client pass the image in a field with name upload['image']

In Rails log, it should be like

Parameters: {"upload"=> { "image" =>#<ActionDispatch::Http::UploadedFile:0x00000002f0e108 @tempfile=# .....}}

Notice how image is inside upload.