AWSのS3にhaskellでアクセスするのに苦戦してます, flycheck-stackはもう不要になってました, optparse-applicativeがいい感じ
haskellのawsパッケージでS3にアクセス
今書いてるwebアプリケーションでは, S3上に置いているファイルへのアクセスにkahing/goofys: a high-performance, POSIX-ish Amazon S3 file system written in Goというファイルシステムにバケットをマウントできるものを使っています. s3fsの高速版です.
しかし, アプリケーションサーバが大量のファイルを送信しているのが負荷になっているのではないかと思ってS3から直接ダウンロードさせるように書き換えようとしています.
ライブラリにはaws :: Stackage Serverを使用しています.
とりあえずダウンロードより先にアップロードを実装しないとデバッグも出来ないなと思ってアップロードを実装していました. 本当はアップロードもブラウザが直接S3と通信する方が良さそうですが, その場合データベースとファイルの一貫性保証がかなり面倒くさいので, ひとまずアプリケーションサーバを経由してアップロードするように書いています.
Aws.simpleAws
を使うとS3なのにhttpsにアクセスしに行って証明書が存在しないというエラーでアクセスできなくて何もわからなくなっていました.
そういう時はサンプルを見れば良いわけです.
サンプルを見れば良いことに気がつくのに3日ぐらいかかった気がします.
aws/PutObjectIA.hs at master · aristidb/awsを見るとプロトコルにHTTPを指定しているのがわかりますね.
READMEのサンプルコードではAws.defServiceConfig
を使ってますが,
putでは使えないのかな,
awsのことがまだよくわかっていないのでよくわかりません.
S3.s3 Aws.HTTP S3.s3EndpointApNorthEast False
をServiceConfiguration
に利用すればとりあえず動きました.
間違いです.
バケット名にドットを使っているせいでした.
ドットが無ければAws.defServiceConfig
で動きました.
aws
のサンプルコードではwithManager
を使っているわけですが,
この関数はhttp-conduit :: Stackage Serverというパッケージに2つあります.
以下2つのモジュールに同名の関数が存在するわけです.
aws
パッケージのサンプルコードで使われているのはNetwork.HTTP.Conduit
の方なのですが,
これはDeprecated
になっているのでNetwork.HTTP.Client.Conduit
のものに適時書き換えましょう.
withManager
を使ってReaderT
内でask
するか,
newManager
を使うかですね.
私はnewManager
を使いました.
今はsimpleAws
を使っています.
Pre-Signed URLを生成する方法がよくわからない
putとdeleteは実装できました. 肝心のgetを実装する必要があります. S3にリダイレクトするように設定して, アプリケーションサーバへの負荷を減らしたいところです. しかし, これがよくわからない.
パブリックアクセス可能ではないのでPre-Signed URLというのを使いたいと思ってやっています.
ファイル名もディレクトリトラバーサル防止のために固定文字列+数値ということになってるので実際のファイル名をresponse-content-disposition
で差し替えたいですし.
S3から直接ダウンロードさせるときにファイル名を差し替える - Qiita
aws cliで生成すればリージョンの問題か何回か手動でリダイレクト(リダイレクトは自動転送だから手動でリダイレクトは矛盾していませんか?)が必要でしたがURLの生成自体は出来ていたので権限の問題がないことは確認済みです. 【小ネタ】AWS CLIでS3のPre-Signed URLを生成できるようになっていました! | Developers.IO
しかしawsパッケージでこれをやる方法がよくわからなくて困っていました.
How do you generate pre-signed object URL (S3) with this library? · Issue #189 · aristidb/aws
では他の人は解決してるみたいですが,
このissueを見てawsUri
関数を使ってもシグネチャとか全く無いURLが生成されるしどういうことなんでしょうか全くわからない.
queryToUrl, SignedQueryあたりを使えば出来るんでしょうか, わからない, 誰か教えて欲しい, 明日やります.
flycheck-stackが不要になっていたので削除しました
stackが登場し始めた頃は, stack無し環境が標準だったので, flycheckはstackで管理しているプロジェクトでは動きませんでした.
なので, chrisdone/flycheck-stack: A flycheck checker that uses stack ghciというものを使っていました. 最近これが動かなくなっていました. 今見てみたら2016年05月が最後のコミットでした.
それで削除してみたらflycheck本体flycheck/flycheck: On the fly syntax checking for GNU Emacsにhaskell-stack-ghc
というソースが追加されててこれが普通に動きました.
というわけで役目を果たしたflycheck-stackはもう不要のようですね. 本体が単体で使えるようになっていてハッピー.
slackとのコマンドライン連携ツールを書いています
optparse-applicativeがいい感じ
2年ぶりぐらいにhaskellでコマンドラインツールを書いてます. 今回言うコマンドラインツールというのは, 単に実行するだけではなく, そこそこオプション指定などをするものを指します.
特に理由がない場合はLibrariesからライブラリを選択しようと思っていたので, コマンドラインオプション解析ツールにはoptparse-applicative :: Stackage Serverを選ぶことにしました. Command line options parsing - optparse-applicative library ドキュメントがすごい書き込まれてて, 昔やってた時より大分良くなってることを実感しました.
slackへのファイル投稿で少し悩んでいた
slackとの連携といえばwebhookを使うと思ってたらfiles.upload method | Slackはwebhookじゃなくてlegacy apiを使わないと出来ないんですね… jsonベースでファイルをやりとりするのは面倒という判断でしょうか.
slackのlegacy apiがhttpステータスでは200
を返してきてbodyのjsonで{ "ok": false }
を返す形式で,
これよくtwitterで話題になるAPIじゃん,
と笑いました.
addRequestHeader
でパラメータを設定しようとしていたので少しハマった,
URLに付けなければいけない.
setRequestQueryString
を使いましょう.
Network.HTTP.Simple
にはPOST bodyにフォームの形式で値を付加する方法がないっぽい?
Network.HTTP.Conduit.urlEncodedBody
が必要ですね.
しかし,
何故かslackに1回だけしかfile
を使った形式での投稿が成功しなかったので,
form形式の投稿はなかったことになり,
content
を使うことにしました.
content
ではなくfile
で投稿できないか30分ぐらい悩みましたがよく考えてみると別にfile
に固執する理由がなかった.
Network.HTTP.Simple
で済ませられるならそれのほうが良いですよね.