Django Rest Framework - 单元测试图片文件上传

8

我正在尝试对我的文件上传REST API进行单元测试。我在网上找到了一些使用Pillow生成图像的代码,但是它不能被序列化。

这是我用于生成图像的代码:

image = Image.new('RGBA', size=(50, 50), color=(155, 0, 0))
file = BytesIO(image.tobytes())
file.name = 'test.png'
file.seek(0)

我尝试上传这个图片文件:
return self.client.post("/api/images/", data=json.dumps({
     "image": file,
     "item": 1
}), content_type="application/json", format='multipart')

然后我收到以下错误:

<ContentFile: Raw content> is not JSON serializable

如何将Pillow图像转换为可序列化的格式?


你尝试过省略 json.dumps 调用吗?在我的 Django 项目中,我只是使用测试客户端将数据作为字典进行提交。 - Brobin
2个回答

8

在这种情况下,我不建议将数据提交为JSON,因为这会使问题变得复杂。只需使用您要提交的参数和文件进行POST请求即可。 Django REST Framework将在无需您将其序列化为JSON的情况下处理它。

我曾经为上传文件到API端点编写过一个测试,看起来像这样:

def test_post_photo(self):
    """
    Test trying to add a photo
    """
    # Create an album
    album = AlbumFactory(owner=self.user)

    # Log user in
    self.client.login(username=self.user.username, password='password')

    # Create image
    image = Image.new('RGB', (100, 100))
    tmp_file = tempfile.NamedTemporaryFile(suffix='.jpg')
    image.save(tmp_file)

    # Send data
    with open(tmp_file.name, 'rb') as data:
        response = self.client.post(reverse('photo-list'), {'album': 'http://testserver/api/albums/' + album.pk, 'image': data}, format='multipart')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

在这种情况下,我使用了 tempfile 模块来存储使用 Pillow 生成的图像。在示例中使用的 with 语法允许您相对轻松地传递请求正文中文件的内容。
基于此,类似以下内容应该适用于您的用例:
image = Image.new('RGBA', size=(50, 50), color=(155, 0, 0))
file = tempfile.NamedTemporaryFile(suffix='.png')
image.save(file)

with open(file.name, 'rb') as data:
    return self.client.post("/api/images/", {"image": data, "item": 1}, format='multipart')

顺便提一下,根据您的使用情况,接受图像数据作为base 64编码字符串可能更加方便。


我在Windows上实现了这个解决方案,一段时间内它能够正常工作,但后来出现了错误PermissionError: [Errno 13] Permission denied: 'C:...。也许需要像https://dev59.com/ZnVC5IYBdhLWcg3w-WSs中回答的那样添加一些额外的函数。 - juliocesar
谢谢您提供的解决方案。一个提示:当我连续运行多个测试时,遇到了很大的麻烦。我总是会收到像“OSError: cannot identify image file "/tmp/tmpci3voa1h"”这样的错误。解决方法:在运行完一个测试后,您需要在文件对象上调用seek(0),例如data.seek(0) - user1383029
“Image” 模块从哪里来的? - user9903
1
@RudolfOlah PIL或Pillow - Matthew Daly

0

你已经将该文件转换为字节,这是不可JSON序列化的。

如果不知道你的API希望接收什么内容,我只能猜测您必须将file编码为字符串:"image": file.decode('utf-8')

虽然有很多解决方案来针对你的一般问题单元测试图片上传到REST API。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接