RubyでSlackのfiles.getUploadURLExternal、completeUploadExternalを使う
files.upload APIを使っていたらSlackからfiles.upload APIは廃止になるから新しい形式に移行してねという連絡がきました。
Action required: Important notice about the files.upload API
めんどくさいなと思いながら対応しないとプログラムが動かなくなってしまうので軽い気持ちで対応したところ、罠があったので大変でした。
2025年11月12日に廃止予定だそうです。
どんな風に変わった?
今までfiles.uploadでファイルをアップロードすることができましたが、files.getUploadURLExternal をつかってアップロード先のURLを取得し(files.getUploadURLExternal)、そのURLに対してファイルをアップロードする(files.completeUploadExternal)二段階形式になりました。
SDKを使わない方法
私はSDKを使って開発していなかったので自力で書くことにしました。
files.getUploadURLExternal
アップロード先のURLを取得します。
| パラメータ名 | 内容 |
|---|---|
| token | SlackのAPIトークン |
| filename | アップロードするファイル名 |
| length | アップロードするファイルのサイズ |
- ソースコード
require 'net/http'
require 'json'
require 'uri'
# Tokenは環境変数から読み出す
token = ENV['SLACK_TOKEN']
file_name = 'upload_test.png'
file = File.binread(file_name) # File.readだとダメ。
params = {
length: file.size,
token: token,
filename: file_name
}
headers = {
'Content-Type' => "application/x-www-form-urlencoded",
'Authorization' => "Bearer #{token}"
}
uri = URI.parse "https://slack.com/api/files.getUploadURLExternal"
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
response = http.start do
request = Net::HTTP::Post.new(uri.path, headers)
request.set_form_data(params)
http.request(request)
end
response_params = JSON.parse(response.body, symbolize_names:true)
p response_params
- レスポンス
{:ok=>true, :upload_url=>"https://files.slack.com/upload/v1/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", :file_id=>"XXXXXXXXXXXXX"}
files.completeUploadExternal
files.getUploadURLExternalで取得したURLに向けてアップロードします。
| パラメータ名 | 内容 |
|---|---|
| token | SlackのAPIトークン |
| files | "[{"id":"F044GKUHN9Z", "title":"slack-test"}]"←これ文字列 |
filesで本当に困ってました。公式のAPI仕様書を見てもarrayと書いてあるので配列渡せばいいのね。と思って配列で渡したらエラーが出るんですよね。調べてみると配列を文字列で渡せばよかったようです。
Array of file ids and their corresponding (optional) titles.
# 配列で渡したときのエラーレスポンス
{"ok"=>false, "error"=>"invalid_arguments", "response_metadata"=>{"messages"=>["[ERROR] must provide an object [json-pointer:/files/0]"]}}
- 続きのコード
channel_id = ENV['SLACK_CHANNEL_ID']
uri = URI.parse response_params[:upload_url]
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
response = http.start do
request = Net::HTTP::Post.new(uri.path, headers)
request.body = file
http.request(request)
end
params = {
token: token,
files: "[{'id': '#{response_params[:file_id]}'}]", # このダブルクォーテーションを外すとエラー。
channel_id: channel_id
}
uri = URI.parse "https://slack.com/api/files.completeUploadExternal"
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
req = Net::HTTP::Post.new uri
req.set_form_data(params.merge({token: token}))
res = http.request(req)
response_body = JSON.parse(res.body)
p response_body
ファイルが中途半端にアップロードされる
上のコードを書けば中途半端にアップロードされることはないのですが、最初書いたときはFile.readでローカルのファイルを読みだしてfiles.getUploadURLExternalのリクエストパラメータ「length」をFile#sizeを使って指定していました。
これをやったところ中途半端しかないファイルがアップロードされてしまいました。File.readの仕様を確認したところ「引数 length が指定された場合はバイナリ読み込みメソッド、そうでない場合はテキスト読み込みメソッドとして動作します。」と書いてありました。テキストモードで読み込んだ結果適切にサイズが計算できていなかったみたいなので、File#binreadでバイナリモードで読み込むように修正しました。
まとめ
files.completeUploadExternalの引数とFileのサイズではまってました。APIの向き先変えるだけだと思ったら、想定よりも時間がかかったので私みたいにはまる人が今後出ないことを祈ります。
Discussion