GoogleCloudStorage
gcp
FormData
axios
signedurl

Google Cloud Storageの署名付きURLに対してaxiosでPUTするときにFormDataを使おうとしてはならない

Google Cloud Platform その2 Advent Calendar 2018の6日目の投稿です。

axiosでファイルアップロードしようとして調べると、FormDataを利用する手順が世の中には数多くあります。

axios/axios: Promise based HTTP client for the browser and node.js

https://github.com/axios/axios

ただ、Google Cloud Storage(GCS)の署名付きURLに対してPUTする場合、FormDataは利用できないので、お気をつけください。


利用できない理由

FormDataは複数項目をまとめてアップロードできる仕組みで、Contnent-Typemultipart/form-data が自動で指定されます。ここまでなら、署名付きURL生成時にContnent-Typemultipart/form-data を指定すれば良いだけなのですが、multipart/form-data の場合、複数項目をアップロードする前提なので、区切り文字Boundary が含まれます。

こいつがリクエスト時にランダムで生成されるため、署名付きURLでContnent-Type に指定するにも指定できず詰みます。(詰みました~


だめな感じの実装(イメージ)


検証につかれて実行可能なコードを用意するのが疲れました

let uploadUrl = '';

await axios.get(`${apiRootUrl}signed_url?filename=${filename}&content_type=${contentType}`)
.then((res) => {
uploadUrl = res.data.signed_url;
})
.catch((error) => {
throw error;
});

const options = {
headers: {
'Content-Type': file.type, // ここで指定しても適用されないTT
},
};

let data = new FormData();
data.append('file', file);
await axios.put(uploadUrl, data, options)
.then((res) => {
console.log('成功!');
})
.catch((error) => {
throw error;
});



いい感じの実装(イメージ)


散々ハマった挙げ句、動いた実装から一部持ってきました

let uploadUrl = '';

await axios.get(`${apiRootUrl}signed_url?filename=${filename}&content_type=${contentType}`)
.then((res) => {
uploadUrl = res.data.signed_url;
})
.catch((error) => {
throw error;
});

const options = {
headers: {
'Content-Type': file.type,
},
};

await axios.put(uploadUrl, file, options)
.then((res) => {
console.log('成功!');
})
.catch((error) => {
throw error;
});



参考

axios/axios: Promise based HTTP client for the browser and node.js

https://github.com/axios/axios

multipart/form-dataのリクエストで地味にハマったメモ - Qiita

https://qiita.com/Zaki_Tk/items/073f597d52f6fd8e3dcd

FormData オブジェクトの利用 - ウェブデベロッパーガイド | MDN

https://developer.mozilla.org/ja/docs/Web/Guide/Using_FormData_Objects