1

I am making a REST API that requires uploading files. For testing purposes, I am uploading files through postman, but I don't know how to access the files on server side. I was able to retrieve the _dict for authentication purposes, but for the file, it returns None.

#arguments - create, delete, view, list, search, upload, download
@app.post("/incident-resource/{service}")
async def incident_resource_service_handler(service, request: Request):
    try:
        session = Session()
        reqDataForm: FormData = await request.form()
        reqData = reqDataForm._dict 

        file = reqDataForm.get('resource')  # in form(key='resource', value=abc.jpeg)
        print(type(file))                   #< class 'NoneType' >

        user = usr_getAuthenticatedUser(session, reqData)
        userRole = getRole(session, user.role_id)
    except Exception as e:
        session.rollback()
        return handleException(e)
Chris
  • 18,724
  • 6
  • 46
  • 80
Thinkal VB
  • 189
  • 3
  • 12
  • Does this answer your question? [Upload file using fastapi](https://stackoverflow.com/a/70657621/17865804) – Chris Jan 21 '22 at 07:20
  • Unfortunately no, it only says about file. I want both the dictionary and file from reqDataForm. Should I change the argument ?? - I don't know yet !! – Thinkal VB Jan 21 '22 at 07:25
  • You'd usually have one argument to the view function for each form parameter; one for the file, a second for an additional field, a third for any additional field after that and so on. – MatsLindh Jan 21 '22 at 09:25
  • Does this answer your question? [How to add both file and JSON body in a FastAPI POST request?](https://stackoverflow.com/questions/65504438/how-to-add-both-file-and-json-body-in-a-fastapi-post-request) – Chris Nov 09 '22 at 16:49

2 Answers2

2

As per the documentation, "files will be uploaded as form data", and you should receive them by either declaring the type in your endpoint as bytes or UploadFile (see this answer and this answer). If you need to send additional data along with the file (e.g., form or JSON data), you may have a look at this answer as well.

If, however, you need to do that in the way you approach the problem in your question, i.e., by parsing the raw request body using request.form(), have a look at Option 1 of this answer and the example below. As per Starlette's documentation, you can get the filename and the contents as shown below. Whatever key names are used to declare the data on client side, you have to use the same key names on server side as well, in order to retrieve the data from the form. Below file is used as the key for the file data.

@app.post('/upload')
async def create_file(request: Request):
    form = await request.form()
    filename = form['file'].filename
    contents = await form['file'].read()

However, it might be best to declare the expected parameters (e.g., files and form data) in your endpoint instead of using request.form(), as it would allow you to define whether or not a parameter should be required (hence, an exception would be raised in case a client does not include such a parameter in their request), as well as their type (i.e., str, int, etc.), which, again, would raise an exception if the client request does not include the data in the correct type. If you define the endpoint with async def, then you could use aiofiles, as shown here, to save the file to disk; otherwise, see this answer.

Upload Single File via HTML form

from fastapi import FastAPI, Request, UploadFile, HTTPException, status
from fastapi.responses import HTMLResponse
import aiofiles

app = FastAPI()


@app.post('/upload')
async def upload(file: UploadFile):
    try:
        contents = await file.read()
        async with aiofiles.open(file.filename, 'wb') as f:
            await f.write(contents)
    except Exception:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail='There was an error uploading the file',
        )
    finally:
        await file.close()

    return {'message': f'Successfuly uploaded {file.filename}'}


# Access the form at 'http://127.0.0.1:8000/' from your browser
@app.get('/')
async def main():
    content = '''
    <body>
    <form action='/upload' enctype='multipart/form-data' method='post'>
    <input name='file' type='file'>
    <input type='submit'>
    </form>
    </body>
    '''
    return HTMLResponse(content=content)

Upload Multiple Files via HTML form

from fastapi import FastAPI, Request, UploadFile, HTTPException, status
from fastapi.responses import HTMLResponse
from typing import List
import aiofiles

app = FastAPI()


@app.post('/upload')
async def upload(files: List[UploadFile]):
    for file in files:
        try:
            contents = await file.read()
            async with aiofiles.open(file.filename, 'wb') as f:
                await f.write(contents)
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail='There was an error uploading the file(s)',
            )
        finally:
            await file.close()

    return {'message': f'Successfuly uploaded {[file.filename for file in files]}'}  


# Access the form at 'http://127.0.0.1:8000/' from your browser
@app.get('/')
async def main():
    content = '''
    <body>
    <form action='/upload' enctype='multipart/form-data' method='post'>
    <input name='files' type='file' multiple>
    <input type='submit'>
    </form>
    </body>
    '''
    return HTMLResponse(content=content)
Chris
  • 18,724
  • 6
  • 46
  • 80
0

In my case no complexity needed or even javascript. ALl I had todo was add enctype to the html form as follows :

    <form method="POST"  enctype="multipart/form-data">
    <div class="mb-3">
      <h5>Medical Data:</h5>
      <input type="file" multiple id="fileid1" placeholder="Job location" name="file" value="{{file}}" class="form-control">
    </div>

FAST API PYTHON FILE

@router.post("/add-medical-record/")
async def create_job(request: Request, file: UploadFile = None, db: Session = Depends(get_db)):