テストツールが,Pythonだった(アップロードのほうが本題)
前提
署名付きURLの発行
インストール
npm install aws-sdk
発行
実際はファイル分けして,バケット名・キー名・有効期限を引数で渡すとURLが返ってくる,といった感じにするはず.
myBucket
バケットのmyDir
フォルダにtest.jpg
という名前で置くとすると,以下のとおり*1:
import { PutObjectCommand, S3Client} from "@aws-sdk/client-s3"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; // S3クライアント const REGION = "ap-northeast-1" const client = new S3Client({ region: REGION }); // 発行 const s3BucketName = "myBucket" const expiresIn = 3600 const s3Key = "myDir/test.jpg" const objectParams = { Bucket: s3BucketName, Key: s3Key } const url = await getSignedUrl(client, new PutObjectCommand(objectParams), { expiresIn });
アップロード
実際にPythonの方からアップロードする.
ファイルの場所やURLなどは適宜置き換え.
urllibパッケージでやる
import urllib.error import urllib.request s3_url = "your/s3/signed/url" img_name = "my/image/file" req_body = None with open(img_name, 'rb') as f: req_body = f.read() req = urllib.request.Request( s3_url, req_body, method="PUT" headers={"Content-Type": "application/octet-stream"} ) try: with urllib.request.urlopen(req) as res: body = res.read() except (urllib.error.HTTPError, urllib.error.URLError) as e: raise e
requestsパッケージでやる(うまくいかない)
requestsパッケージを使うとすると,以下のとおり:
import requests s3_url = "your/s3/signed/url" img_name = "my/image/file" img = open(img_name, 'rb') file = {'upload_file': img} headers={"Content-Type": "application/octet-stream"} res = requests.put(s3_url, headers=headers, files=file) res.raise_for_status()
file
のキー名はなんでもいいけど,これでS3にputして,S3に上げた画像を見ると画像が開けない.
ファイルをテキストで確認すると以下のようになっている*2:
--da72d2d3252f4f373c7a588dda07173c Content-Disposition: form-data; name="upload_file"; filename="test.jpg" ~~~よくわからない文字列~~~ --da72d2d3252f4f373c7a588dda07173c--
これをバイナリエディタで途中から見ると以下のとおり:
... 6A 70 67 22 0D 0A 0D 0A FF D8 FF E0 00 10 4A 46 49 46 00 ...
1行目はjpg"
と2行改行(CRLF x2).2行目は,分かる人は分かると思うけどjpgのヘッダ.
このような形で,画像ファイルが壊れたと言うよりは,requestsで送信する際のヘッダとフッタが付いたままのjpgになってしまっている.
基本的な用法として,サーバ側の処理として
file = req.files["upload_file"]
などとしてファイルを受け取ってやるらしいので,その際にヘッダとフッタが除去される(はず).
しかしS3バケットにあげようとすると,除去される前の画像をそのまま上げてしまって,だめ.
そもそもdictにしなければいいとなるけれど,requests.put()
だとdictで送る必要がある.
じゃあrequests.post()
ならとなるけれど,そうすると403 Forbiddenが返ってくるので結局だめ.
おわりに
これって,壊れてる.