【问题标题】:Is there a way to stream data directly from python request to minio bucket有没有办法将数据直接从 python 请求流式传输到 minio 存储桶
【发布时间】:2020-01-18 14:47:38
【问题描述】:

我正在尝试向服务器发出 GET 请求以检索 tiff 图像。然后我想使用 MinIO python SDK 中的 put_object 方法将它直接流式传输到 MinIO。

我知道我可以通过将图像保存到临时文件然后上传来做到这一点,但我想看看我是否可以跳过这一步。

我尝试直接插入字节响应并使用 BytesIO 来包装它,但我认为我遗漏了一些东西。

r = requests.get(url_to_download, stream=True)
Minio_client.put_object("bucket_name", "stream_test.tiff", r.content, r.headers['Content-length'])

我得到以下错误

AttributeError: 'bytes' 对象没有属性 'read'

非常感谢任何帮助!

【问题讨论】:

  • 嗨@JudsonCrouch,如果我的回答解决了您的问题,请点击复选标记考虑accepting it。这向更广泛的社区表明您已经找到了解决方案,并为回答者和您自己提供了一些声誉。没有义务这样做。

标签: python python-3.x stream python-requests minio


【解决方案1】:

在 MinIO 上阅读 documentation 关于 put_object,有一些示例如何将新对象添加到对象存储服务器。这些示例仅说明如何添加文件。

这是put_object函数的定义:

put_object(bucket_name, object_name, data, length, content_type='application/octet-stream', metadata=None, progress=None, part_size=510241024)

我们对data 参数感兴趣。它指出:

任何实现 io.RawIOBase 的 python 对象。

RawIOBase 是原始二进制 I/O 的基类。它还定义了方法read

如果我们使用dir() 内置函数来尝试返回r.content 的有效属性列表,那么我们可以检查read 是否存在:

'read' in dir(r.content) -> 返回False

这就是您收到AttributeError: 'bytes' object has no attribute 'read' 的原因。这是因为type(r.content)bytes 类。


您可以将r.content 转换为继承自RawIOBase 的类。也就是说,使用io.BytesIO 类。要获取对象的大小(以字节为单位),我们可以使用io.BytesIO(r.content).getbuffer().nbytes

因此,如果您想将原始字节数据流式传输到您的存储桶,请将 bytes 类转换为 io.BytesIO 类:

import io
import requests

r = requests.get(url_to_download, stream=True)
raw_img = io.BytesIO(r.content)
raw_img_size = raw_img.getbuffer().nbytes

Minio_client.put_object("bucket_name", "stream_test.tiff", raw_img, raw_img_size)

注意:示例显示从文件中读取二进制数据并通过从 stat_result 读取 st_size 属性来获取其大小,该属性是使用 os.stat() 函数返回的。

st_size 等价于io.BytesIO(r.content).getbuffer().nbytes

【讨论】:

  • url_to_download 是什么意思?那里应该添加什么?像例如localhost:5000/streamToMinio?我尝试了您的解决方案,因为我必须处理类似的问题,但我得到了 InvalidSchema 异常
  • OP 已询问如何将 tiff 图像直接流式传输到 MinIO 而无需先下载。 url_to_download 将是此 tiff 图像所在的端点。
【解决方案2】:

您可以像这样将文件直接流式传输到 minio 存储桶中:

import requests

from pathlib import Path
from urllib.parse import urlparse

from django.conf import settings
from django.core.files.storage import default_storage

client = default_storage.client
object_name = Path(urlparse(response.url).path).name
bucket_name = settings.MINIO_STORAGE_MEDIA_BUCKET_NAME

with requests.get(url_to_download, stream=True) as r:
    content_length = int(r.headers["Content-Length"])
    result = client.put_object(bucket_name, object_name, r.raw, content_length)

或者你可以直接使用一个django文件字段:

with requests.get(url_to_download, stream=True) as r:
    # patch the stream to make django-minio-storage belief
    # it's about to read from a legit file
    r.raw.seek = lambda x: 0
    r.raw.size = int(r.headers["Content-Length"])
    model = MyModel()
    model.file.save(object_name, r.raw, save=True)

来自 Dinko Pehar 的 RawIOBase 提示非常有帮助,非常感谢。但是您必须使用 response.raw 而不是 response.content,因为它会立即下载您的文件,并且在尝试存储大型视频时非常不方便。

【讨论】:

    猜你喜欢
    • 2019-09-28
    • 1970-01-01
    • 2021-12-19
    • 2020-02-28
    • 1970-01-01
    • 1970-01-01
    • 2021-04-11
    • 2019-09-08
    • 2011-07-17
    相关资源
    最近更新 更多