Vue.js로 이미지 만들기, Rails의 Active Storage로 저장

Rails 애플리케이션에서나는 js로 그림을 만드는 프로그램을 만들고 있는데, 막힌 부분이 많아서 기록을 남기고 싶다.
대략적으로 절차를 설명하면 다음과 같다.
  • Vue.js + Konva.jscanvas를 사용하여 탭으로 도형 만들기
  • 기본 64 형식의 데이터 URL로 변환
  • Rails의 Active Storage를 통해 저장
  • 개발 환경

  • Rails 6.1.4
  • Vue 3.1.5
  • Kova8.1.1canvas 태그로 도형 등을 그리는 JavaScript 라이브러리)
    Konva - JavaScript 2d canvas library
  • 환경 구조 등의 부분을 생략하다

    Vue와 Kova로 이미지를 만들고 Data URL로 변환하여 이미지 표시


    이번에 Chart모형_form.html.erb에서 Vue를 그렸어요.
    views/charts/_form.html.erb
      <div id="js-chart"></div>
    
    javascript/chart.js
    import { createApp } from 'vue'
    import App from './chart.vue'
    
    document.addEventListener('DOMContentLoaded', () => {
      const selector = '#js-chart';
      if(document.querySelector(selector)){
        createApp(App).mount(selector);
      }
    })
    
    chart.vue
    <template>
      <div id="chart"></div>
    ~ 中略 ~
      <img id="img">
    </template>
    
    공식.에 샘플 코드가 첨부되어 있으며 각종 도형의 그리는 방법이 있으니 사용하지 마십시오.
    chart.vue
    // data関数でstageとimageUrlを定義しておく
    data() {
      return {
        stage: '',
        imageUrl: ''
      }
    }
    
    chart.vue
    this.stage = new Konva.Stage({
      width: 500,
      height: 800,
      container: 'chart' // template内の<div id="chart"></div>のid名を指定して紐付ける
    });
    
    기본 인스턴스stage를 생성하고 도면층과 도면 인스턴스를 추가하면 이미지canvas를 조합하여 만들 수 있습니다.
    예제)
  • stage
  • background_layer
  • base_line
  • main_layer
  • circle1
  • circle2
  • chart.vue
    // toDataURLで 'data:image/png;base64' で始まるData URLを返す
    this.imageUrl = this.stage.toDataURL 
    
    // template内の<img id="img">に作成した画像を表示させる
    const img = document.getElementById("img")
    img.src = this.imageUrl
    

    Rails를 통해 제작된 이미지의 데이터 URL 수신


    데이터 URL의 값을 Vue 측면의 hidden input에 바인딩하고 Rails의 form_with에 생성된 양식과 연결
    views/charts/_form.html.erb
    + <%= form_with model: chart, multipart: true, id: 'form' do %>
        <div id="js-chart"></div>
    + <% end %>
    
    chart.vue
    + <input type="hidden" name="chart[image]" id="chart_image" :value="imageUrl">
    
    + <a @click="save">Save</a>
    
    ~中略~
    
    methods: {
    +   save() {
    +    const promise = new Promise(function(resolve) {
          this.imageUrl = this.stage.toDataURL 
    +      resolve()
    +    })
    +    function onFulfilled() {
    +      const form = document.getElementById('form')
    +      form.submit()
    +    }
    +    promise.then(onFulfilled)
    +  }
    }
    
    이렇게 하면 Save 단추를 누르면 이미지의 데이터 URL이 params에서 수신될 수 있다

    받은 데이터 URL에서 Active Storage로 저장


    받은 Data URL을 간단하게 처리해야 하기 때문에 더 잘 예측할 수 있도록 반을 나누었습니다.
    image_blob.rb
    class ImageBlob
      attr_reader :image_data_url
    
      def initialize(image_data_url)
        @image_data_url = image_data_url
      end
    
      def mime_type
        image_data_url[%r/(image\/[a-z]{3,4})/]
      end
    
      def to_io
        StringIO.new(decoded_content)
      end
    
      private
    
      def decoded_content
        Base64.decode64(content)
      end
    
      def content
        image_data_url.sub(%r/data:image\/.{3,},/, '')
      end
    end
    
    chart.rb
    class Chart < ApplicationRecord
      has_one_attached :image
    
      def attach_blob(image_data_url)
        image_blob = ImageBlob.new(image_data_url)
        image.attach(
          io: image_blob.to_io,
          filename: Time.zone.now,
          content_type: image_blob.mime_type #content_typeは無くてもOK
        )
      end
    end
    
    charts_controller.rb
    class ChartsController < ApplicationController
    
      def create
        @chart = Chart.new(chart_params)
        @chart.attach_blob(image_data_url)
    
        if @chart.save
          redirect_to @chart, notice: 'Saved!'
        else
          render :new, status: :unprocessable_entity
        end
      end
    
      private
    
      def image_data_url
        params.require(:chart).permit(:image)[:image]
      end
    end
    
    이상입니다.
    제가 많이 찾아봤는데 <input type="file">에 올린 방법의 글은 기본적으로 참고할 수 있는 것이 적지만 드디어 이루어졌습니다.
    제작된 이미지를 Active Storage로 직접 저장하는 용례는 드물 것 같은데, 누군가에게 도움이 된다면 좋겠다.
    보관소:
    obregonia1/visible_scratch_skillz
    참조:
    Active Storage의 개요-rails 가이드
    Active Storage를 사용하여 base64에서 인코딩된 이미지 저장 - Qita

    좋은 웹페이지 즐겨찾기