# FastAPI 文件上传

```python
# coding: utf8

from fastapi import FastAPI, File, UploadFile
from typing import List

app = FastAPI()

#  上传文单个件方法一，  File 是直接继承自 Form 的类。
@app.post('/file')
async def create(file: bytes = File(...)):
    return {"file_size": len(file)}


# 上传文单个件方法二
@app.post('/uploadfile')
async def upload_file(file: UploadFile = File(...)):
    # contents = await file.read()
    # print(contents)
    return {'filename': file.filename}

# 上传多个文件方法一
@app.post("/files")
async def create(fileS: List[bytes] = File(...)):
    return {"file_sizes": [len(file) for file in fileS]}


# 上传多个文件方法二
@app.post("/uploadfiles")
async def upload_file(fileS: List[UploadFile] = File(...)):
    return {"filenames": [file.filename for file in fileS]}

# # 启动: uvicorn main:app --reload
```

> pip install python-multipart

### 上传和form表单组合使用

````python
- 上传单个多个文件
```python
from fastapi import FastAPI, File, Form, UploadFile
app = FastAPI()

@app.post("/files/")
async def create_file(
    file: bytes = File(...), one: UploadFile = File(...),
        token: str = Form(...)
):
    return {
        "filesize": len(file),
        "token": token,
        "one_content_type": one.content_type,
    }
````

* 上传多个文件

```python
from fastapi import FastAPI, File, Form, UploadFile
from typing import List

app = FastAPI()


@app.post("/files/")
async def create_file(
    file: bytes = File(...), one: List[UploadFile] = File(...),
        token: str = Form(...)
):
    return {
        "filesize": len(file),
        "token": token,
        "one_content_type": [file.content_type for file in one],
    }
```

```
## UploadFile 的属性如下：

- filename：上传文件名字符串（str），例如， myimage.jpg；

- content_type：内容类型（MIME 类型 / 媒体类型）字符串（str），例如，image/jpeg；

- file： SpooledTemporaryFile（ file-like 对象）。其实就是 Python文件，可直接传递给其他预期 file-like 对象的函数或支持库。

- UploadFile 支持以下 async 方法，（使用内部 SpooledTemporaryFile）可调用相应的文件方法。

- write(data)：把 data （str 或 bytes）写入文件；

- read(size)：按指定数量的字节或字符（size (int)）读取文件内容；

- seek(offset)：移动至文件 offset （int）字节处的位置；

例如，await myfile.seek(0) 移动到文件开头；
执行 await myfile.read() 后，需再次读取已读取内容时，这种方法特别好用；
close()：关闭文件。

因为上述方法都是 async 方法，要搭配「await」使用。

例如，在 async 路径操作函数 内，要用以下方式读取文件内容

`contents = await myfile.read()`
```

### 文件上传保存

```bash
pip install python-multipart
```

<pre class="language-bash"><code class="lang-bash">import os
import shutil
from pathlib import Path
from typing import Union, Any
from tempfile import NamedTemporaryFile
from fastapi import APIRouter, Depends, File, UploadFile

from api.common.logger import logger
f<a data-footnote-ref href="#user-content-fn-1">rom core.config import settings</a>
from api.utils import response_code

router = APIRouter()


@router.post("/upload/file/", summary="上传图片")
async def upload_image(
        file: UploadFile = File(...)
):
    logger.info(f"用户{token_data.sub}->上传文件:{file.filename}")

    # 本地存储临时方案，一般生产都是使用第三方云存储OSS(如七牛云, 阿里云)
    save_dir = f"{settings.BASE_DIR}/assets"
    if not os.path.exists(save_dir):
        os.mkdir(save_dir)

    try:
        suffix = Path(file.filename).suffix

        with NamedTemporaryFile(delete=False, suffix=suffix, dir=save_dir) as tmp:
            shutil.copyfileobj(file.file, tmp)
            tmp_file_name = Path(tmp.name).name
    finally:
        file.file.close()

    return response_code.resp_200(data={"image": f"http://127.0.0.1:8010/assets/{tmp_file_name}"})

</code></pre>

[^1]: 
