7
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

URLから取得した画像ファイルを直接multipartでPOSTできるか試す【Ruby】

Last updated at Posted at 2019-01-03

あけましておめでとうございます!
今年の目標は「半人前卒業」の筆者です。よろしくお願いします。

というわけで、早速昨年末から持ち越した宿題を消化します。
何かというと、GoogleChartで作成したQRコードの画像を、ローカルに保存せず直接APIに投げようとしたところ、思いの外ハマってしまった件。
備忘録も兼ねてまとめていきます。あいかわらず稚拙な文章ですがご容赦ください。

前提

  • open-uriを使ってURLから画像ファイルを操作する

    (今回はGoogle chartで画像を作成する際のURLを使います)
  • multipart/form-dataのリクエストで画像ファイルをAPIに送る

環境

Ruby : 2.4.2
Rails : 5.2.0

Net/Httpを用いて実装を試みるもエラー

最初の実装はこんな感じ。
(参考:覚え書き: Ruby で net/http を使ってファイルなどを multipart で POST する)

qrcode.rb
require 'open-uri'
require 'net/http'

def qrcode_request
  image_url = "http://chart.apis.google.com/chart?xxx"
  url = URI.parse("#{Settings.api.url}") #settings.ymlに記載されたAPIのURL
  file = open(image_url)
  data = [ "file", file ]
  req = Net::HTTP::Post.new(url.path)
  req.set_form(data, "multipart/form-data")
  req.basic_auth('username', 'password')

  res = Net::HTTP.start(url.host, url.port) do |http|
    http.request(req)
  end
end

# 実行結果
# no implicit conversion of nil into String excluded from capture: DSN not set

エラーについて調べてみると、似た状況について触れている以下のページを発見しました。
【ruby】openメソッドで画像を開いた時にno implicit conversion of nil into Stringエラーが発生する。
これによると、OpenURIの仕様で、10KB以下のファイルを選択するとStringIOクラスのオブジェクト、それ以上だとTempFileが生成されることに起因するエラーのようです。

こちらを参考にTempFileを生成するよう修正し、もう1度試します。
結果…変化せず。

HTTPクライアントを変えてみる

上記の方法で改善しなかった原因はよくわからないまま、とにかくパラメータを上手く送る方法がないかな〜ということで、Net/Http以外のHTTPクライアントを導入した実装方法を試します。

Faraday

gem install faraday
qrcode_faraday.rb
def qrcode_request_faraday
  image_url = "http://chart.apis.google.com/chart?xxx"

  conn = Faraday.new(:url => "#{Settings.api.url}") do |builder|
      builder.request  :multipart
      builder.use Faraday::Request::BasicAuthentication, "username","password"
      builder.adapter Faraday.default_adapter
  end

  res = conn.post, {
        :image => OpenURI.open_uri(image_url) { |io|
          Faraday::UploadIO.new(io,'image/png', 'qr.png')
        }
      }
end

実行結果…API側で400エラー。

Rest-Client

gem install rest-client
qrcode_rest_client.rb
def qrcode_request_rest_client
  image_url = "http://chart.apis.google.com/chart?xxx"
  @url = "#{Settings.api.url}"
  file = open(image_url)

  request = RestClient::Request.new(
      :method => :post,
      :url => @url,
      :user => "username",
      :password => "password",
      :payload => {
        :multipart => true,
        :image => file
    })
  response = request.execute
end

実行結果…こちらも400エラー。

OpenURIの仕様を調べる

HTTPクライアントの変更で解決しなかったので、OpenURI周辺をもう一度調べてみたところ、OpenURIはそもそもGET以外のメソッドを使えない仕様であるという情報を入手しました。
How do I make a POST request with open-uri? -Stack Overflow

一応、POSTができない問題を解消するパッチは存在するようです。
open-uriは便利だがGETしかできないので何とかする話

しかし、この中でmultipart/formdataを指定して画像ファイルを直接リクエストとして送る方法は残念ながら見つかりませんでした。

結論

URLから取得した画像をローカルに保存せず直接APIに投げるときは、multipartは使わず、画像をエンコードしてJSONなどで送信し、API側でデコードするのがベターなようです。
より良い方法をご存知の方はコメントいただければ幸いです…。

2019/01/18追記

multipart-postというgemを導入したところ、画像ファイルを直接リクエストとしてうまく送ることができました。

gem install multipart-post 
qrcode_multipart_post.rb
require 'net/http/post/multipart'

def qrcode_request_multipart_post
  image_url = "http://chart.apis.google.com/chart?xxx"
  url = URI.parse("#{Settings.api.url}")
  file = open(image_url)

  req = Net::HTTP::Post::Multipart.new(url.path, "image" => UploadIO.new(file, "image/png", "qr.png"))
  req.basic_auth("username", "password")

  res = Net::HTTP.new(url.host, url.port).start do |http|
    http.request(req)
  end
end

どうやらOpenURIのPOSTも可能なようです。
API側でCarrierWaveを導入すれば、受け取った画像ファイルの表示、アップロードなどが可能になります。

7
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?