본문 바로가기

Python

[Python] fastapi 파일 업로드 & data

반응형

fastapi로 파일을 받고 json 데이터도 받고 싶었다.

근데 안된단다... 그걸 모르고 열심히 삽질했다.

422 Error value is not a valid dict FormData([('files', <starlette.datastructures.UploadFile object at 0x7fc770987610>), (
'new_report', '{\r\n "name": "test.jpeg"\r\n,"owner": "test11"\r\n}')])

안되는 이유는 아래와 같다.

다수의 File과 Form 매개변수를 한 경로 작동에 선언하는 것이 가능하지만, 요청의 본문이 application/json가 아닌 multipart/form-data로 인코딩 되기 때문에 JSON으로 받아야하는 Body 필드를 함께 선언할 수는 없습니다.
이는 FastAPI의 한계가 아니라, HTTP 프로토콜에 의한 것입니다.

https://fastapi.tiangolo.com/ko/tutorial/request-forms-and-files/

 

폼 및 파일 요청 - FastAPI

폼 및 파일 요청 File 과 Form 을 사용하여 파일과 폼을 함께 정의할 수 있습니다. 정보 파일과 폼 데이터를 함께, 또는 각각 업로드하기 위해 먼저 python-multipart를 설치해야합니다. 예 ) pip install pyth

fastapi.tiangolo.com

 

내가 하고 싶은 코드

# models.py
class test_report_model(Base):
  __tablename__ = "test"
  __table_args__ = {'comment': '테스트db'}
  id = Column(INTEGER(unsigned=True), primary_key=True, autoincrement=True, nullable=False, comment='PK')
  file_name = Column(String(255), nullable=False, comment='파일명')
  owner = Column(String(50))

# schemas.py
class test_report_create(Base):
  file_name : str
  owner: str

# router.py
router = APIRouter(
  prefix="/test"
)

# 에러남
@router.post("")
async def test(files: List[UploadFile] = File(...), new_report: schemas.test_report_create = Form(...)):
"""
new_port는 test_report_create 모양으로 받고싶다.
"""
	print(new_report)

422 Error
value is not a valid dict
FormData([('files', <starlette.datastructures.UploadFile object at 0x7fc770987610>), ('new_report', '{\r\n   "name": "test.jpeg"\r\n,"owner": "test11"\r\n}')])

 

 

파일과 데이터를 같이 받으려고 하는 경우 2가지의 선택지가 있다.

 

1. str 로 받기

# router.py
router = APIRouter(
  prefix="/test"
)

@router.post("")
async def test(new_report: str, files: List[UploadFile] = File(...)):
	print(new_report)
    # 변환 필요
    new_report_dict = json.loads(new_report)
    new_report_obj = schemas.test_report_create(**new_report_dict)
	print(new_report_obj)

"{'name': 'test.jpeg','owner': 'test11'}"
{'name': 'test.jpeg','owner': 'test11'}

2. 하나하나 받기

# router.py
router = APIRouter(
  prefix="/test"
)

# 하나하나 나열해야함
@router.post("")
async def test(name: str, owner: str, files: List[UploadFile] = File(...)):
	print(name, owner)

 

선택은 자유!

반응형