Как загрузить файл с multipart / form POST, имеющий только URL-адрес файла, который нужно загрузить (куски) [дубликат]


На этот вопрос уже есть ответ здесь:

Есть ли шанс загрузить файл через конечную точку API, которая принимает multipart / form-data в качестве типа контента, имеющего только URL этого файла?

Правило: Загрузить весь файл в память, а затем загрузить с помощью этой конечной точки невозможно (Нет никакой гарантии, что коробка когда-либо будет достаточно большой, чтобы вместить временный файл).

Вопрос: Я хочу передавать файл кусками с одного сервера (GET) на другой (multipart/form-data POST). Возможно ли это? Как этого добиться?

Поток: file_server сервер загрузки

Вот простой пример загрузки в память (ОЗУ) опции (но это противоречит правилу):

from io import BytesIO

import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder

file_url = 'https://www.sysaid.com/wp-content/uploads/features/itam/image-banner-asset.png'
requested_file_response = requests.get(file_url, stream=True)

TOKEN_PAYLOAD = {
    'grant_type': 'password',
    'client_id': '#########',
    'client_secret': '#########',
    'username': '#########',
    'password': '#########'
}


def get_token():
    response = requests.post(
        'https://upload_server/oauth/token',
        params=TOKEN_PAYLOAD)
    response_data = response.json()
    token = response_data.get('access_token')
    if not token:
        print("token error!")
    return token

token = get_token()

file_object = BytesIO()
file_object.write(requested_file_response.content)

# Form conctent
multipart_data = MultipartEncoder(
    fields={
        '--': (
            'test.png',
            file_object  # AttributeError: 'generator' object has no attribute 'encode' when I try to pass generator here.
        ),  
        'id': '2217',
        'fileFieldDefId': '4258',
    }
)

# Create headers
headers = {
    "Authorization": "Bearer {}".format(token),
    'Content-Type': multipart_data.content_type
}

session = requests.Session()
response = session.post(
    'https://upload_server/multipartUpdate',
    headers=headers,
    data=multipart_data,
)

Ответ находится в файле, похожем на создание объекта для потока цели

Большое Спасибо за любую помощь. Ура!

1 3

1 ответ:

Если я читаю requests_toolbelt исходный код справа, то это требует не только способности .read() файла (который мы могли бы получить просто передав requests.get(..., stream=True).raw), но и того, что есть некоторый способ определить, сколько данных осталось в потоке.

Предполагая, что вы уверены, что у вас всегда есть допустимый Заголовок content-length, я бы предложил следующее решение:

import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder

file_url = 'https://www.sysaid.com/wp-content/uploads/features/itam/image-banner-asset.png'
target = 'http://localhost:5000/test'


class PinocchioFile:
    """I wish I was a real file"""

    def __init__(self, url):
        self.req = requests.get(url, stream=True)
        length = self.req.headers.get('content-length')
        self.len = None if length is None else int(length)
        self._raw = self.req.raw

    def read(self, chunk_size):
        chunk = self._raw.read(chunk_size) or b''
        self.len -= len(chunk)
        if not chunk:
            self.len = 0
        return chunk


multipart_data = MultipartEncoder(
    fields={
        '--': (
            'test.png',
            PinocchioFile(file_url),
        ),
        'id': '2217',
        'fileFieldDefId': '4258',
    }
)

# Create headers
headers = {
    'Content-Type': multipart_data.content_type
}

response = requests.post(
    target,
    data=multipart_data,
    headers=headers,
)