rootstrap / active-storage-base64

Base64 support for ActiveStorage
https://rootstrap.com
MIT License
161 stars 16 forks source link

Question: sending JSON to API #72

Closed huylv closed 1 year ago

huylv commented 1 year ago

Hi,

My code looks like this:

Model:

class Image < ApplicationRecord
  has_one_base64_attached :image_file
end

Controller:

class ImagesController < ApplicationController
  def create
    @image = Image.new(image_params)

    if @image.save
      render json: @image, status: :created, location: @image
    else
      render json: @image.errors, status: :unprocessable_entity
    end
  end

  def image_params
    params.require(:image).permit(image_file: :data)
  end

Now I want to send a JSON to the API, I've tried different ways, none seems to work properly. What should it look like? Thank you.

sebastiancaraballo commented 1 year ago

Hello @huylv, does this work?

{
  "image": {
    "data": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII"
  }
}
huylv commented 1 year ago

Hi @sebastiancaraballo,

With that payload, there is a server error Unpermitted parameter: :data Looks like there is an image being created, too:

  TRANSACTION (0.4ms)  begin transaction
  ↳ app/controllers/api/latest/images_controller.rb:22:in `create'
  Image Create (0.6ms)  INSERT INTO "images" ("image_file", "created_at", "updated_at") VALUES (?, ?, ?)  [["image_file", nil], ["created_at", "2023-02-27 18:16:53.682491"], ["updated_at", "2023-02-27 18:16:53.682491"]]
  ↳ app/controllers/api/latest/images_controller.rb:22:in `create'
  TRANSACTION (1.8ms)  commit transaction

but the image_file is nil.

sebastiancaraballo commented 1 year ago

@huylv My bad, please try:

{
  "image": {
    "image_file": {
      "data": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII"
    }
  }
}
huylv commented 1 year ago

Hi @sebastiancaraballo,

That seems to work:

Parameters: {"image"=>{"image_file"=>{"data"=>"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII"}}}
  TRANSACTION (0.1ms)  begin transaction
  ↳ app/controllers/api/latest/images_controller.rb:22:in `create'
  Image Create (0.6ms)  INSERT INTO "images" ("image_file", "created_at", "updated_at") VALUES (?, ?, ?)  [["image_file", nil], ["created_at", "2023-02-27 18:31:10.614818"], ["updated_at", "2023-02-27 18:31:10.614818"]]
  ↳ app/controllers/api/latest/images_controller.rb:22:in `create'
  ActiveStorage::Blob Load (0.1ms)  SELECT "active_storage_blobs".* FROM "active_storage_blobs" INNER JOIN "active_storage_attachments" ON "active_storage_blobs"."id" = "active_storage_attachments"."blob_id" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 46], ["record_type", "Image"], ["name", "image_file"], ["LIMIT", 1]]
  ↳ app/controllers/api/latest/images_controller.rb:22:in `create'
  ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 46], ["record_type", "Image"], ["name", "image_file"], ["LIMIT", 1]]
  ↳ app/controllers/api/latest/images_controller.rb:22:in `create'
  ActiveStorage::Blob Create (0.2ms)  INSERT INTO "active_storage_blobs" ("key", "filename", "content_type", "metadata", "service_name", "byte_size", "checksum", "created_at") VALUES (?, ?, ?, ?, ?, ?, ?, ?)  [["key", "zbrhq3qk0vw017xw0lphk21b7jq6"], ["filename", "1677522670"], ["content_type", "image/png"], ["metadata", "{\"identified\":true}"], ["service_name", "local"], ["byte_size", 89], ["checksum", "N61FwSA10M6m8QLyjDQMUA=="], ["created_at", "2023-02-27 18:31:10.630371"]]
  ↳ app/controllers/api/latest/images_controller.rb:22:in `create'
  ActiveStorage::Attachment Create (0.2ms)  INSERT INTO "active_storage_attachments" ("name", "record_type", "record_id", "blob_id", "created_at") VALUES (?, ?, ?, ?, ?)  [["name", "image_file"], ["record_type", "Image"], ["record_id", 46], ["blob_id", 36], ["created_at", "2023-02-27 18:31:10.632332"]]
  ↳ app/controllers/api/latest/images_controller.rb:22:in `create'
  Image Update (0.1ms)  UPDATE "images" SET "updated_at" = ? WHERE "images"."id" = ?  [["updated_at", "2023-02-27 18:31:10.635156"], ["id", 46]]
  ↳ app/controllers/api/latest/images_controller.rb:22:in `create'
  TRANSACTION (1.5ms)  commit transaction
  ↳ app/controllers/api/latest/images_controller.rb:22:in `create'
  Disk Storage (0.9ms) Uploaded file to key: zbrhq3qk0vw017xw0lphk21b7jq6 (checksum: N61FwSA10M6m8QLyjDQMUA==)
[ActiveJob] Enqueued ActiveStorage::AnalyzeJob (Job ID: 4e211a24-90dc-4f3a-aecd-5014a0555b2a) to Async(default) with arguments: #<GlobalID:0x000000010f425dc8 @uri=#<URI::GID gid://rails-api-noob/ActiveStorage::Blob/36>>
Completed 500 Internal Server Error in 30ms (ActiveRecord: 3.0ms | Allocations: 7807)

NoMethodError (undefined method `image_url' for #<Api::Latest::ImagesController:0x00000000010888>):

However, the image_file is still nil as you can see in the first INSERT statement. Also, the error undefined method `image_url' probably indicates its redirected location is not available? Do I need to provide content_type and/or file_name?

sebastiancaraballo commented 1 year ago

@huylv I don't think that should be necessary. Can you provide me more context about image_url? Another way to check if the image is actually attached using rails console: Image.last.image_file.attached?

huylv commented 1 year ago

Hi @sebastiancaraballo,

I just uploaded the whole source code here. It's super simple, there are only several files. The image_url as far as I understand comes from the part location: @image in the controller (see the code I posted above).

irb(main):001:0> Image.last.image_file.attached?
  Image Load (0.5ms)  SELECT "images".* FROM "images" ORDER BY "images"."id" DESC LIMIT ?  [["LIMIT", 1]]
  ActiveStorage::Attachment Load (0.7ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 46], ["record_type", "Image"], ["name", "image_file"], ["LIMIT", 1]]
=> true

I just started learning Rails from some Youtube tutorials so everything is new to me 😊

sebastiancaraballo commented 1 year ago

@huylv Great, that's a good start. I see the image is attached now so I'm closing this issue. I recommend using a gem like jbuilder or similar to declare views and specify the attributes to return.

huylv commented 1 year ago

@sebastiancaraballo: Thank you for your help. Is the image already stored as an image rather than a base64 string?