はじめに
Reactの初学者です。
画像のアップロードと保存機能を実装する際に結構詰まったので、備忘録として残しておきます。
フロントエンドはReact、バックエンドはFastAPIで実装します。
コードは初心者っぽいところが多いと思われます。ご了承ください。
React
まずは全体のコード。
formタグ内で画像を選択した後、Submitボタンを押すとAPIリクエストが送信されます。
my-app/src/components/SubmitImage.jsx
import React from 'react'
import { useState } from 'react'
import 'bulma/css/bulma.min.css' // css
export const SubmitImage = () => {
const [image, setImage] = useState()
const [errorMessage, setErrorMessage] = useState('')
const URL = 'http://127.0.0.1:8000/images/' //適宜設定
const getImage = (e) => {
if(!e.target.files) return
const img= e.target.files[0]
setImage(img)
}
const Submit=async()=>{
const formdata = new FormData()
formdata.append('upload_file', image)
const requestOptions={
method:"POST",
body:formdata,
}
const response =await fetch(URL,requestOptions)
const data=await response.json()
}
const handleSubmit=(e)=>{
e.preventDefault()
Submit()
}
return (
<div>
<form className="box" onSubmit={handleSubmit}>
<label className="label" htmlFor="img">画像</label>
<div>
<input id="img" type="file" accept="image/*,.png,.jpg,.jpeg,.gif" onChange={getImage}/>
</div>
<br/>
<button className="button is-primary" type="submit">Submit</button>
</form>
<ErrorMessage message={errorMessage}></ErrorMessage>
</div>
)
}
ポイントはここ。
const Submit=async()=>{
const formdata = new FormData()
formdata.append('upload_file', image)
const requestOptions={
method:"POST",
body:formdata,
}
const response =await fetch(URL,requestOptions)
const data=await response.json()
}
-
FormDataオブジェクトにuploadされた画像をappendする。
- このときのkey='upload_file'はFastAPI側で使用します。
-
requestOptionsにはheadersを指定しない。
-
"Content-Type":"multipart/form-data"
は記載しないほうが良い。 - 指定すると 400 Bad Requestを吐かれます。
-
見た目
FastAPI
続いてバックエンド側
api/main.py
from fastapi import FastAPI,UploadFile
import shutil
app=FastAPI()
# ~~~~~~ 省略 ~~~~~~~~
@app.post("/images/")
def get_uploadfile(upload_file: UploadFile): # フロント側のFormDataのkeyに合わせる(upload_file)
path = f'api/files/{upload_file.filename}'# api/filesディレクトリを作成しておく
with open(path, 'wb+') as buffer:
shutil.copyfileobj(upload_file.file, buffer)
return {
'filename': path,
'type': upload_file.content_type
}
FastAPIに画像を保存する機能は無く、shutilでコピーして保存するようです。
つまづいた点
フロント側とバックエンド側でformDataのkey値を揃える必要がある。
すなわち、
- React
formdata.append('upload_file', image)
- FastAPI
def get_uploadfile(upload_file: UploadFile):
のように、上記の例ではupload_fileと揃える。
これに気づかず、422 unprocessable entity を永遠に吐かれていました。。。