graphene-django如何接收文件输入?UploadMutation上传失败排查
graphene-django GraphQL文件上传失败排查与实现方案
问题原因
- 缺少文件上传依赖与中间件配置:graphene-django默认仅解析
application/json类型请求,无法识别multipart/form-data格式的文件上传请求,未配置对应解析逻辑时,文件参数不会被传递到Mutation的mutate方法中。 - 代码逻辑错误:
mutate方法接收到的file参数是Django封装的UploadedFile文件对象,不是字符串路径,直接调用.endswith()会抛出属性错误,无法正常返回结果;代码中存在拼写错误formate,正确拼写为format。 - 调用方式错误:GraphQL文件上传不支持直接将文件内容写在查询语句文本中,必须遵循multipart请求规范拆分参数提交,直接在mutation语句中写
file: xxx无法传递真实文件内容。 - 未正确注册Schema:如果关闭了graphene的自动驼峰转换,查询语句中的
uploadFile与定义的upload_file字段不匹配,会直接报字段不存在错误;若未将FileUploadMutation注册到根Mutation类型,接口也无法识别该字段。
正确实现步骤
1. 安装依赖
pip install graphene-file-upload
2. 修改Django配置
首先在settings.py中添加应用:
INSTALLED_APPS = [ # 原有其他应用 "graphene_django", "graphene_file_upload", ]
然后修改urls.py中的GraphQL路由,替换默认视图为支持文件上传的视图:
from django.urls import path from graphene_file_upload.django import FileUploadGraphQLView # 替换为你自己项目的schema导入路径 from .schema import schema urlpatterns = [ # 原有其他路由 path("graphql", FileUploadGraphQLView.as_view(graphiql=True, schema=schema)), ]
3. 修正Mutation代码
import graphene from graphene_file_upload.scalars import Upload from graphql import GraphQLError class UploadMutation(graphene.Mutation): class Arguments: file = Upload(required=True) success = graphene.Boolean() def mutate(self, info, file, **kwargs): # 取文件对象的name属性判断后缀 if not file.name.lower().endswith(".xlsx"): raise GraphQLError('File format should be "xlsx".') # 此处可添加自定义文件保存逻辑,示例: # import os # save_path = os.path.join("your_upload_dir", file.name) # with open(save_path, "wb+") as f: # for chunk in file.chunks(): # f.write(chunk) return UploadMutation(success=True) class FileUploadMutation(graphene.ObjectType): upload_file = UploadMutation.Field() # 必须将Mutation注册到Schema根类型,示例: # class Query(graphene.ObjectType): # test = graphene.String() # # class Mutation(FileUploadMutation, graphene.ObjectType): # pass # # schema = graphene.Schema(query=Query, mutation=Mutation)
正确调用方式
文件上传必须使用multipart/form-data格式提交请求,请求体包含三个固定部分:
operations:JSON格式,存储GraphQL查询语句与变量,文件对应变量位置先填null占位map:JSON格式,映射表单中文件字段到variables内的对应路径- 文件字段:键与map中定义的键一致,值为待上传的本地文件
前端Fetch调用示例
// 取input选择的文件 const file = document.querySelector("input[type=file]").files[0]; const operations = JSON.stringify({ query: ` mutation UploadFile($file: Upload!) { uploadFile(file: $file) { success } } `, variables: { file: null } }); const map = JSON.stringify({ "file_field": ["variables.file"] }); const formData = new FormData(); formData.append("operations", operations); formData.append("map", map); formData.append("file_field", file); fetch("/graphql", { method: "POST", body: formData, // 不要手动设置Content-Type,让浏览器自动生成带boundary的请求头 }) .then(res => res.json()) .then(res => console.log(res));
Postman调用步骤
- 请求方法选择POST,地址填写你的GraphQL接口地址
- Body面板选择
form-data类型 - 添加三个表单项:
- 键为
operations,类型选Text,值为上述operations对应的JSON字符串 - 键为
map,类型选Text,值为上述map对应的JSON字符串 - 键为
file_field,类型选File,值选择本地待上传的xlsx文件
- 键为
- 直接发送请求即可
注意事项
- 不要手动为multipart请求设置
Content-Type请求头,必须让客户端自动生成带boundary分隔符的头,否则服务端无法解析参数 - 若项目开启了CSRF校验,需要给接口添加CSRF豁免或在请求中携带合法CSRF token,否则会被Django拦截
- 上传大文件时,提前调整
settings.py中FILE_UPLOAD_MAX_MEMORY_SIZE、DATA_UPLOAD_MAX_MEMORY_SIZE参数,避免请求被拦截
内容的提问来源于stack exchange,提问作者user8598113
相关产品推荐
相关产品推荐

