RailsのActive StorageでFilePondを使う方法
FilePondは、Rik Schenninkによって書かれた、ファイルアップロードを処理するための美しいJavaScriptライブラリです。
私は、スムーズなアップロード体験と優れたUIビジュアルを求める様々なRuby on Railsプロジェクトでこのライブラリを使用してきました。
この記事では、FilePondをRailsと統合する方法を説明し、進めながら技術的な選択について説明します。このチュートリアルはActive Storageの基本的な知識を前提としています。Railsのこの部分に馴染みがない場合は、https://guides.rubyonrails.org/active_storage_overview.htmlの公式Railsガイドをご覧ください。
このチュートリアルのデモコードはhttps://github.com/Code-With-Rails/filepond-demoにあります。そのリポジトリをクローンして、そこからデモを実行することをお勧めします。
何を構築するか
FilePondをアプリに統合するには、2つの部分があります:
- まず、FilePond JavaScriptライブラリを追加し、ファイル入力HTMLタグで有効にする必要があります。
- 次に、FilePondをRailsアプリと統合する必要があります。具体的には、Active Storageにファイルアップロードを処理させたいです。
最初の部分を達成するために、バニラJavaScriptを使用します。2番目の部分では、既存のActive StorageのJavaScriptライブラリを活用し、ダイレクトアップロードを有効にします。FilePondが必要とする特定のサーバーエンドポイントに対応するために、カスタムコントローラを作成します。
FilePondのインストール
このチュートリアルでは、Rails 7.0.xアプリケーションから始めます。これは、JavaScript依存関係を追加するためにimportmap-railsを使用することを意味します。
bin/bundle add importmap-rails
bin/rails importmap:install
次に、以下のコマンドでFilePondの依存関係を追加する必要があります:
bin/rails importmap pin filepond
FilePondをプリロードしたいので、config/importmap.rbファイルを次のように修正します:
# 変更前
pin 'filepond', to: 'https://ga.jspm.io/npm:filepond@4.30.4/dist/filepond.js'
# 変更後
pin 'filepond', to: 'https://ga.jspm.io/npm:filepond@4.30.4/dist/filepond.js', preload: true
FilePondの基本
FilePondを初期化するには、まずHTMLファイル入力要素が必要です。フォームは次のようになります:
<%= form_with model: @user, url: update_avatar_path, method: :post do |f| %>
<%= f.label :avatar, 'アバターを更新' %>
<!-- この入力をpondに変換します -->
<%= f.file_field :avatar, class: 'filepond', direct_upload: true %>
<%= f.button '更新' %>
<% end %>
対応するJavaScriptコードは次のようになります:
// application.js
const input = document.querySelector('.filepond')
FilePond.create(input)
これが、FilePondがシンプルなファイル入力タグをウィジェットに変換するために必要な最小限のものです。
ここで構築しているものに特化して、以下を実装します:
- FilePondを設定して(Active StorageのJavaScriptライブラリを使用して)クラウドプロバイダーへのダイレクトアップロードを実行
- FilePondとアプリを設定してリモートURLのみでのアップロードを許可
- ユーザーがFilePondウィジェットでキャンセル(元に戻す)をクリックしたときに未添付ファイル(blob)をパージするようにアプリを設定
シンプルなRailsアプリケーション
まず、Active Storageがファイル添付を関連付けられるようにデータモデルをセットアップします。
bin/rails g model User name:string
bin/rails db:migrate
user.rbファイルで、モデルを次のように更新しましょう:
class User < ApplicationRecord
has_one_attached :avatar do |attachable|
attachable.variant :thumb, resize_to_limit: [100, 100]
end
end
FilePondの統合
Active Storageのダイレクトアップロード機能を使用するようにFilePondを設定しましょう:
// app/javascript/application.js
FilePond.setOptions({
server: {
process: (fieldName, file, metadata, load, error, progress, abort, transfer, options) => {
const uploader = new DirectUpload(file, directUploadUrl, {
directUploadWillStoreFileWithXHR: (request) => {
request.upload.addEventListener(
'progress',
event => progress(event.lengthComputable, event.loaded, event.total)
)
}
})
uploader.create((errorResponse, blob) => {
if (errorResponse) {
error(`エラーが発生しました:${errorResponse}`)
} else {
const hiddenField = document.createElement('input')
hiddenField.setAttribute('type', 'hidden')
hiddenField.setAttribute('value', blob.signed_id)
hiddenField.name = input.name
document.querySelector('form').appendChild(hiddenField)
load(blob.signed_id)
}
})
return { abort: () => abort() }
},
fetch: {
url: './filepond/fetch',
method: 'POST'
},
revert: {
url: './filepond/remove'
},
headers: {
'X-CSRF-Token': document.head.querySelector("[name='csrf-token']").content
}
}
})
fetchとrevertエンドポイントには、カスタムコントローラが必要です:
# app/controllers/filepond_controller.rb
require 'open-uri'
class FilepondController < ApplicationController
def fetch
uri = URI.parse(raw_post)
url = uri.to_s
blob = ActiveStorage::Blob.create_and_upload!(
io: URI.open(uri),
filename: URI.parse(url).path.parameterize
)
if blob.persisted?
redirect_to rails_service_blob_path(blob.signed_id, blob.filename)
else
head :unprocessable_entity
end
end
def remove
signed_id = raw_post
blob = ActiveStorage::Blob.find_signed(signed_id)
if blob
blob.purge
head :ok
else
head :not_found
end
end
private
def raw_post
request.raw_post
end
end
そしてルート:
# config/routes.rb
Rails.application.routes.draw do
post 'filepond/fetch', to: 'filepond#fetch'
delete 'filepond/remove', to: 'filepond#remove'
end
結論
FilePondは、ユーザーのアップロードにフィードバックを提供するための優れたユーザーインターフェースを提供します。RailsのActive Storageとの統合は難しくありませんが、少しのカスタマイズが必要です。
このチュートリアルは、FilePondを統合する方法の一つを提示し、できるだけバニラRailsを適用しています。次のパートでは、上記の実装方法を使用して、これをすべてgemに変換し、毎回再実装することなく再利用できるようにします。