LoginSignup
12
7

More than 3 years have passed since last update.

React FCで画像ファイルを『Content-type: multipart/form-data』でPOSTする方法(axios)

Last updated at Posted at 2020-03-22

案件でPOSTする際、
『Content-type: form-data』で送信する機会があったので、まとめます。
ボタン部分はマテリアルUIを使っています、初見の方は細かく気にしなくても大丈夫です

環境

react 16.12.0
typescript 3.7.3
material-ui/core 4.8.0(Buttonに使用)

したいこと

inputで選択したファイルをstateにセット、セットしたファイルを POSTする

全体


const IconUpload: FC = () => {

  const [userIconFormData, setUserIconFormData] = useState<File>()

  const handleSetImage = (e: ChangeEvent<HTMLInputElement>) => {
    if (!e.target.files) return
    const iconFile:File = e.target.files[0]
    setUserIconFormData(iconFile)
  }

  const handleSubmitProfileIcon = () => {
    const iconPram = new FormData()
    if (!userIconFormData) return

    iconPram.append('user[icon]', userIconFormData)
    axios
      .post(
        'https://api/update',
        iconPram,
        {
          headers: {
            'content-type': 'multipart/form-data',
          },
        }
      )
  }

  return (          
     <form>
       <p>アイコンアップロード</p>
       <input
         type="file"
         accept="image/*,.png,.jpg,.jpeg,.gif"
         onChange={(e: ChangeEvent<HTMLInputElement>) => handleSetImage(e)}
       />
       <Button
         text="変更する"
         variant="contained"
         color="primary"
         type="button"
         onClick={handleSubmitProfileIcon}
         disabled={userIconPreview === undefined}
       />
    </form>
  )
}

export default IconUpload

切り分けて解説

form周り、ボタン部分

  <form>
       <p>アイコンアップロード</p>
       <input
         type="file"
         accept="image/*,.png,.jpg,.jpeg,.gif"
         onChange={(e: ChangeEvent<HTMLInputElement>) => handleSetImage(e)}
       />
       <Button
         text="変更する"
         variant="contained"
         color="primary"
         type="button"
         onClick={handleSubmitProfileIcon}
         disabled={userIconPreview === undefined}
       />
    </form>

input

  • type:fileにすることによってアップロードが可能になる
  • accept:アップロード画面で、ここに指定された拡張子のファイルのみ選択可能になる
  • onChange:通常のinputと異なり、valueにはFileを入れることはできない、なので別の場所に取得したFileを保持するfunctionを呼び出す

Button

  • onClick:onChangeでセットしたstateをaxiosでPOSTするfunctionを呼び出す

ファイルを取得するfunction

const handleSetImage = (e: ChangeEvent<HTMLInputElement>) => {
  if (!e.target.files) return
  const iconFile:File = e.target.files[0]
  setUserIconFormData(iconFile)
}

if (!e.target.files) return
TypeScriptでは、undefindになる可能性のある値に関してはエラーがでるので先に無い場合はreturnすることを明示的にしている

const iconFile:File = e.target.files[0]
これがファイル本体。
filesは複数選択可能の場合を備え、配列になっていて、[0]としてあげないと取得できない。
型はFile。

setUserIconFormData(iconFile)
stateにセット

ちなみに、e.target.files[0]をURLにしてimgに入れたいとなると

const blobUrl = URL.createObjectURL(iconFile)

blob:http://パスに変換され、URLをしてとして扱うことができます。

ファイルをaxiosでPOSTするfunction

const createProfileIcon = () => {
    const iconPram = new FormData()
    if (!userIconFormData) return

    iconPram.append('user[icon]', userIconFormData)
    axios
      .post(
        'https://api/update',
        iconPram,
        {
          headers: {
            'content-type': 'multipart/form-data',
          },
        }
      )
  }

ようやくAPI送信部分
content-type: multipart/form-dataで送信する際、気をつけること2つ

1. FormDataという形式で送ってあげなければいけない。

const iconPram = new FormData()
FormDataオブジェクトを作成
型はそのまんまで、FormData

iconPram.append('user[icon]', userIconFormData)
作成したFormDataオブジェクトにパラメーターを追加
append(キー, 送信したいファイル)

2. headersに'content-type': 'multipart/form-data'を指定

基本API通信する時はjsonが多いかと思いますが、jsonでは正しく送れず、空になるので注意

_axios.createを使って、Classでまとめている場合にも、

className.defaults.headers = { 'Content-Type': 'multipart/form-data' }

で、上書きしてあげましょう

送信内容

スクリーンショット 2020-03-22 16.48.47.png

こんな感じです。

確認ポイント

  1. RequestHeaderscontent-typemultipart/form-dataになっている
  2. FormDataのvalueが(binary)になっている

まとめ

これで正常にmultipart/form-dataでファイルをPOSTできるようになりました!
WEBサービスを作成する際、画像のアップロードはよく出でくるシチュエーションだとおもいますので、参考になれば幸いです

初めてこの機能を実装する際には迷う部分がたくさんあって、ファイルのPOSTする形も何種かあると思っていましたが、2種類のみのようです。

  • Fileのまま送信(multipart/form-data)
  • Base64にエンコードして送信(application/json)

エンコードするとjsonのまま送れるメリットがあるのですが、ファイルサイズが20~30%あがるそうです。
やればやるほど奥が深く、正解がなくて迷いますが、楽しいですね

12
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
7