API を使用したローカル画像のアップロード

こんにちは皆さん、

ここが適切な場所かどうかわからないのですが、質問させてください。私は、別のフォーラムから新しい Discourse インスタンス(移行したい社内ナレッジ投稿)へデータセットをインポートする作業を行っています。

古いデータは複数の JSON 構造にきれいにまとめられているのですが、古い投稿に関連する各種画像のアップロードでつまずいています。

discourse_api gem を使用しており、これまでのところ非常に順調に進んでいますが、この gem が不完全であることは承知しています。画像がどこかホストされている場合、以下のようにすれば正常にアップロードできます。

client.upload_file({'user_id':24,url:'http://valid.url.here/foo.jpg','synchronous':true})

しかし、ローカルマシンに保存されている画像をアップロードしようとすると、行き詰まってしまいます。

client.upload_file({'user_id':24,file:'/local/path/to/image/foo.jpg','synchronous':true})

上記を実行すると、以下のようなエラーが返ってきます。

DiscourseApi::UnprocessableEntity: {"failed"=>"FAILED", 
"message"=>"undefined method 'tempfile' for 
#<String:0x00007f409d2fa398>"} from /home/user/.rvm/gems/
ruby-2.3.7/gems/discourse_api-0.36.0/lib/discourse_api/
client.rb:154:in 'handle_error'

少し調べてみたところ、リクエストが正しく形成されるようにするために、Base64 変換などの何らかの処理が必要なのではないかと気づきました(もしかすると、Ruby gem のアップロードメソッド自体がこの種のアップロードに対応していない可能性もあります。その場合は仕方ありませんが)。

主に、この問題に直面したことがある方がいるか、あるいは私が全くの方向違いの道を進んでいるのではないかという確認のためのガイダンスを求めています。よろしくお願いいたします。

何か理由がない限り、JSON ファイルを使用するインポーターを確認するために、インポートスクリプトディレクトリをご覧になることをお勧めします。API を使うよりも簡単です。

こんにちは、トニーさん。

API を使ってローカルファイルをアップロードするのは、少し手間がかかるかもしれませんね。これで解決するはずです:

# upload_image.rb
require 'discourse_api'
require 'fileutils'

client = DiscourseApi::Client.new("Discourse サイトの URL")
client.api_key = "あなたの API キー"
client.api_username = "ユーザー名"

filename = ARGV[1] # ファイルへのフルパス /home/tony/mypic.png

args = {
  :file => Faraday::UploadIO.new(filename, 'image/png')
}

resp = client.upload_file(args)

url = resp['url']
width = resp['width']
height = resp['height']



# topic_id は、画像をアップロードしたいトピックの ID です。
args = {
  :topic_id => ARGV[0],
  :raw => "<img src=\"#{url}\" width=\"#{width}\" height=\"#{height}\">"
}

# これにより、指定されたトピックに新しい投稿が作成されます
resp = client.create_post(args)

このスクリプトは Faraday gem を使用して、upload_file に必要な正しいファイル形式を提供します。ファイルをアップロードした後、トピックや投稿に割り当てないと表示されず、自動的に削除されてしまいます。

このスクリプトは以下のように呼び出せます:

ruby upload_image.rb <topic_id> <filename>
ruby upload_image.rb 128 /home/tony/mypic.png

ブレイク、

本当にありがとうございます。Faraday ギャムが何をしているのか理解するために、まずそれを調べてから、あなたのスクリプトを試してみます。お時間を割いていただき、心から感謝しています。

  • トニー

アップロードエンドポイントを Python スクリプトから呼び出す方法を理解するために、かなりの時間を費やしました。まず、Chrome 開発者ツールを使ってアップロード API 呼び出しをリバースエンジニアリングするという推奨に従いました。次に、Postman を通じてそのアップロード呼び出しを再現しました。私にとって時間を要したのは、それを動作する Python コードに変換する部分でした。何らかの理由で、Postman が生成した Python コードも動作しませんでした。以下が、私にとって最終的に動作した方法です:

requests.post('/uploads.json', files={'files[]': open('/path/to/image.png', 'rb')}, data={'type': 'composer'})