0

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 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for your answer, but either I'm missing out on something or it doesn't work. params.permit(:upload) yields ActiveRecord::UnknownAttributeError in Api::UploadsController#create unknown attribute 'upload' for Upload. with Postman, and using DataService.add("uploads", upload[image]) gives a console output Error: upload is not defined and does not execute the POST.
Sorry it should be params.permit(:image). Updated my answer. I don't know how DataService.add works but from the error it seems that you should wrap upload[image] in quotes.
I fixed it already, but your answer set me on the right track. It was params.permit(:image) and in routes.rb I had to add resources :images to make it work. I'm editing my question with another small thing I found for anyone else who might have the same problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.