如何通过curl发送包含JSON对象和图像文件的multipart/form-data?如何在Django Rest Framework中处理?
比如我在终端里试了这个命令:
curl -F "profileImage=@/home/user/image.jpg" -F "{'firstName':'hello'};type=application/json" http://127.0.0.1:8000/api/v1/signup/
然后我得到了这样的 request
对象:
print request.FILES
# <MultiValueDict: {u'profileImage': [<InMemoryUploadedFile: image.jpg (image/jpeg)>]}>
print request.DATA
# <QueryDict: {u"{'firstName':'hello'};content-type": [u'application/json']}>
图片没问题,但 QueryDict 表示得不对——整个 JSON 文件都变成了一个键,而内容类型成了值。
在 Django 中使用这些解析器:
parser_classes = (MultiPartParser, FormParser, JSONParser,)
我必须通过 JSON 结构发送文本数据。
5 个回答
这里是:
curl -vvv -X POST -H "Content-Type:multipart/form-data" -H "Accept:application/json" -H -F "username=sample" -F "password=something" -F "image=@Mercury.gif" http://127.0.0.1:8000/api/objects
你不需要手动输入接受 application/json。Django 会自动把这些当作字典对象来处理。
打印 request.DATA 和 request.FILES 会给你以下内容:
<QueryDict: {u'username': [u'sample'] , u'password': [u'something']}>
<MultiValueDict: {u'image': [<InMemoryUploadedFile: Mercury.gif (image/gif)>]}>
指定内容类型的参数就是写“type=”
curl -F "profileImage=@/home/user/image.jpg" -F "{'firstName':'hello'};type=application/json" http://127.0.0.1:8000/api/v1/signup/
不过,我觉得这样可能不太能让JSON解析器处理这些信息……不过你可以试试看 :)
我想出了一个这样的解决方案:
class MultiPartJSONParser(parsers.MultiPartParser):
def parse(self, stream, media_type=None, parser_context=None):
dataAndFiles = super(MultiPartJSONParser, self).parse(stream, media_type, parser_context)
try:
jsonData = json.loads(dataAndFiles.data.get('data'))
except ValueError as exc:
raise parsers.ParseError('JSON parse error - %s' % six.text_type(exc))
# make data mutable, insert json data, and remove raw data
dataAndFiles.data = data = dataAndFiles.data.copy()
data.update(jsonData)
del data['data']
return dataAndFiles
这个方案假设你发送的JSON部分是作为一个叫做'data'的表单字段发送的。
如果你想让这个过程更高级一点,你可以根据请求中的Accept头部的媒体类型来解析data字段。或者,你也可以把数据作为文件上传,然后根据文件的内容类型来解析。
注意,要使用这个解析器类,你需要设置以下默认选项:
REST_FRAMEWORK = {
'FORM_METHOD_OVERRIDE': None,
'FORM_CONTENT_OVERRIDE': None,
}
这是因为这个bug: https://github.com/tomchristie/django-rest-framework/issues/1346
如果你想用 multipart/form-data
这种格式发送数据,就不能同时指定 application/json
。解决这个问题的简单方法是把表单数据以 URL 编码的格式发送。你可以试试这个命令:
curl -F "profileImage=@/home/user/image.jpg" -F "firstName=hello" http://127.0.0.1:8000/api/v1/signup/
谢谢你,xjtian - 你在评论里发的链接加上一些研究让我找到了解决办法。
如果你想发送json数据和文件,最好分成两个请求。第一个请求会在后台创建一个对象,并给你的应用一个ID。第二个请求则是用这个ID来更新对象,上传文件。想了解更多信息,可以点击这里。
如果你对数据类型没有特别要求,可以通过“multipart/form-data”来发送请求。
感谢大家的回答,你们真的帮了我,但我觉得这个方法更适合REST架构。