概要
Expo + React-Nativeでカメラロールにある画像や動画をS3にアップロードしようとしたところ、
色々ハマったのでメモ
- expo + react-native
- カメラロールへのアクセスは
ImagePicker
を使う - S3は署名付きURLを使ってアップロード
- axiosを使う
注)
S3署名付きURLとS3へのアップロードの部分についてはこの記事ではあまり触れません。
試している環境はあらかじめ、署名付きURLを発行するApi Gateway経由のLambdaで作成してあり、それを使っています
ハマったところ
ImagePickerで取得したデータがBlobじゃない
サブタイトルのとおり。
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
quality: 1
})
こんな感じでファイルを取得するが、 result
にはBlobを返してくれない
export declare type ImagePickerResult = {
cancelled: true;
} | ({
cancelled: false;
} & ImageInfo);
export declare type ImageInfo = {
uri: string;
width: number;
height: number;
type?: 'image' | 'video';
exif?: {
[key: string]: any;
};
base64?: string;
};
file uri
は返却されているので、これを使ってBlobに変換する。
uri -> blobは fetch
を使うことで変換できる
(React-Native周りでblob変換してくれるいいライブラリがないのでfetchで代用した)
const blob = await fetch(result.uri).then(r => r.blob())
axiosでBlobをS3にputしたらなぜかJSONファイルがアップロードされた
変換してできたBlobをaxiosでPUTしたら、S3にアップロードはされるものの
極端にサイズが小さい・・・
ダウンロードしてみると、JSONがアップロードされていた。なぜ・・・
試しに fetch
でputしてみたらちゃんとアップロードされた
// これはちゃんとアップロードされる
await fetch(signedurl, {
method: 'PUT',
body: blob
})
// これだとblobでなくてJSON(!?)がアップロードされる
await axios.put(signedurl, blob, {})
悩みに悩んだ末、axiosがrequest bodyのblobを勝手に JSON.stringify()
で
parseしているんじゃないかと推察し、configでparseをやめさせてみたところうまくいった!
// request bodyはそのまま使え
await axios.put(signedurl, blob, {
transformRequest: data => data,
})
出来上がり
抜粋版のソース
// カメラロールから選択したファイルを取得
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
quality: 1
})
// ファイルをblobに変換
const blob = await fetch(result.uri).then(r => r.blob())
// S3署名付きURLの取得
const signedurl = await axios.get('[s3の署名付きURLを取得するためのURL]')
// S3にアップロード
await axios.put(signedurl, blob, {
transformRequest: data => data,
})