🧛
XMLHttpRequestではmultipart/form-dataを明示しない方が良い
自身でmultipart/form-dataの形式にエンコードできる場合は問題ありませんが、FormDataを使用する場合はXMLHttpRequest.setRequestHeaderでContentType: multipart/form-data
を指定するべきではありません。本来Content Typeに付与されるboundaryがsetRequestHeaderで指定された文字列に上書きされるためサーバー側でパース出来なくなってしまいます。
request.open("POST", url)
request.setRequestHeader("Content-Type", "multipart/form-data")
setRequestHeaderで指定した場合
Content-Typeを指定しない場合
Sample code
React.js
const Form: FC = ({ url }) => {
const form = useRef()
...
const send = useCallback(() => {
const request = new XMLHttpRequest()
const data = new FormData(form.current)
request.open("POST", url)
request.send(data)
}, [url])
...
return (
<form ref={form}>
...
</form>
)
}
余談
以前公開した記事でXMLHttpRequestを使用する場合CloudStorage側でcorsの対応をするだけで利用することができます。
file.generateSignedPostPolicyV4
で使用するbucketに対して以下のjsonを用意します。
cors_policy.json
[
{
"origin": ["https://example.com"],
"responseHeader": ["Content-Type"],
"method": ["POST"],
"maxAgeSeconds": 3600
}
]
gsutil cors set bucket_cors_policy.json gs://[適用したい bucket id]
上記の設定をすることでform tagのみでは出来なかったアップロードの進捗等のイベントをXMLHttpRequestを通じて監視することができました。
React.js
const Form: FC = ({ url }) => {
const form = useRef()
...
const send = useCallback(() => {
const request = new XMLHttpRequest()
const data = new FormData(form.current)
request.open("POST", url)
request.addEventListener("progress", (e) => {
console.log(e.loaded ? (e.loaded / e.total) * 100 : 0, "%")
})
request.send(data)
}, [url])
...
return (
<form ref={form}>
...
</form>
)
}
Discussion