Fastapi文件上传那些事?
一、 存在的问题
在AI应用的过程中,我们需要处理用户上传的文件或者知识库。
具体的思路如下:
首先,选用fastapi写好接口来接收文件。文件分为纯文本文件和非纯文本文件,图片和PDF扫描件(这个需要使用ocr或多模态大模型处理,本章先不处理)
本章解决常用的文件类型如下:txt/md/html/ pdf(非扫描件)/docx/doc/xlsx/xls/csv
如何通过接口实现文件上传和提取文本信息。
二、相关概念
我的设计:
文件上传——》使用临时文件接收文件(根据不同的文件类型确定是否需要获得编码格式【utf-8,gdk】)——》使用自定义的MultiFormatProcessor类,获取文本信息——》转换成md格式信息或langchain的document信息块用于后续大模型的上下文信息。
2.1 我们通过fastapi传输的文件,都是二进制的字节流
txt的文件字节流如下:
docx的文件字节流如下:
xlsx的文件字节流如下:
通过这两个截图,可以很好的发现,txt文档的二进制字节流是有具体的编码格式的,比如utf-8,gdk。但是docx、xlsx的文件字节流没有编码格式。
下面是我识别二进制文件内容的编码格式的代码。使用的是chardet包
def check_file_encoding(file_path: str) -> str:import chardettry:with open(file_path, 'rb') as f:raw_data = f.read()result = chardet.detect(raw_data)encoding = result['encoding']return encodingexcept Exception as e:raise HTTPException(status_code=400,detail=f"识别文件的编码格式发生错误{e}")
2.2 为什么会造成上面的情况
为什么同样是文件,txt文件的字节流有编码格式,docx、xlsx的字节流没有编码格式
以下内容由AI生成:
一、TXT 文件:字节流依赖外部编码规则
-
纯文本的本质
TXT 文件仅存储字符的原始字节序列,不含任何元数据(如字体、颜色)。字符的解析完全依赖外部指定的编码规则。例如:- 字节
0xD6 0xD0
在 GBK 编码下表示“中”,在 UTF-8 下则是无效字节。 - 编码不确定性:同一字节序列在不同编码下解析结果可能完全不同,因此必须显式指定编码(如 UTF-8、GBK)。
- 字节
-
无内部标识机制
TXT 文件通常无文件头声明编码(除 UTF-8 BOM 等特例),需通过以下方式推断:- 文件头检测:如 UTF-8 的
EF BB BF
、UTF-16 的FF FE
。 - 规则匹配:通过字节模式判断是否符合 UTF-8 或 GBK 的编码规律(如中文字符的字节特征)。
- 文件头检测:如 UTF-8 的
-
乱码根源
。
若读取编码与保存编码不一致,直接导致乱码(如用 UTF-8 打开 GBK 文件)
📁 二、DOCX/XLSX 文件:结构化封装文本
-
二进制容器结构
DOCX/XLSX 本质是 ZIP 压缩包,内含多个 XML 文件、资源(图片、样式)等:- 文本存储在 XML 中:如 DOCX 的
document.xml
,XLSX 的sheet1.xml
。 - XML 自带编码声明:XML 文件头部明确指定编码(如
<?xml version="1.0" encoding="UTF-8"?>
),文本按此规则解析。
- 文本存储在 XML 中:如 DOCX 的
-
格式自描述性
- 文本与元数据绑定:字体、语言等属性直接嵌入 XML 标签(如
<w:rPr><w:lang w:val="zh-CN"/></w:rPr>
),无需外部猜测编码。 - 统一编码规范:Office Open XML 标准要求内部文本统一使用 UTF-8 或 UTF-16,工具库(如
python-docx
)自动处理。
- 文本与元数据绑定:字体、语言等属性直接嵌入 XML 标签(如
-
无需用户干预
解析工具(如 Python 的docx
库)直接读取 ZIP 结构并解码 XML,用户无需关心底层字节流的编码问题
2.3 文件类型与编码处理总结表
文件类型 | 是否需显式处理编码 | 解析方案 | 工具 |
---|---|---|---|
TXT/MD/HTML | ✅ 是 | 动态检测或手动指定编码 | chardet 、codecs |
CSV | ✅ 是 | 指定编码读取结构化文本 | pandas 、csv |
PDF(非扫描) | ❌ 否 | 库自动提取文本 | pdfplumber |
DOCX/XLSX | ❌ 否 | 库解析结构化内容(UTF-8) | python-docx 、pandas |
DOC/XLS | ⚠️ 需转换 | 先转新格式再解析 | libreoffice |
2.4 实际开发中,应该怎么做?
我们看fastapi源码可以知道,UploadFile类中有下面这几个类属性。
file: Annotated[
BinaryIO,
Doc("The standard Python file object (non-async)."),
]
filename: Annotated[Optional[str], Doc("The original file name.")]
size: Annotated[Optional[int], Doc("The size of the file in bytes.")]
headers: Annotated[Headers, Doc("The headers of the request.")]
content_type: Annotated[
Optional[str], Doc("The content type of the request, from the headers.")
]
处理fastapi上传文档的思路就是:
1. 根据UploadFile.content_type来确定是否需要调用2.1中的check_file_encoding函数来确定编码格式。
2. 自定义一个MultiFormatProcessor类,专门处理以字节形式写入到文件的临时文件。
3. MultiFormatProcessor的话,可以根据2.3的表格来处理不同类型格式的文件。
我个人是通过两个类来处理上传的文件,仅供参考:
一个类是ChatFile,接收UploadFile对象。主要是用来1.验证UploadFile文件的大小、数量是否符合项目的需求。2.使用临时文件来存储上传的文件 3.类方法中使用chardet来检测应该检测的文件类型的编码格式。 4.调用MultiFormatProcessor类得到临时文件的文本信息或者md格式的文本信息。
另一个类是MultiFormatProcessor,接收一个文件的绝对路径和编码格式。这个类的主要功能是,根据不同的文件路径的文件类型,调用不同的方法得到对应的md格式的文字信息和langchain格式的文字块。
三、 参考文章:
二进制文件和文本文件2—— 为什么乱码
python 二进制写入utf8_mob649e8157aaee的技术博客_51CTO博客
python中如何查看文件的编码格式 – PingCode