Разбор строки многочастных данных

У меня есть строка (base64 декодированная здесь), которая выглядит так:

----------------------------212550847697339237761929
Content-Disposition: form-data; name="preferred_name"; filename="file1.rtf"
Content-Type: application/rtf

{\rtf1\ansi\ansicpg1252\cocoartf1504\cocoasubrtf830
{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
{\*\expandedcolortbl;;}
\margl1440\margr1440\vieww10800\viewh8400\viewkind0
\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0

\f0\fs24 \cf0 testing123FILE1}
----------------------------212550847697339237761929
Content-Disposition: form-data; name="to_process"; filename="file2.rtf"
Content-Type: application/rtf

{\rtf1\ansi\ansicpg1252\cocoartf1504\cocoasubrtf830
{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
{\*\expandedcolortbl;;}
\margl1440\margr1440\vieww10800\viewh8400\viewkind0
\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0

\f0\fs24 \cf0 testing123FILE212341234}
----------------------------212550847697339237761929--

Я генерирую это на простой веб-странице, которая загружает пару файлов в AWS Lambda script через запрос PUT с помощью API Gateway. Следует отметить, что то, что я получаю из API-шлюза, это строка Base64, которую затем декодирую в строку выше.

Строка выше - это данные, которые моя Lambda script получает от шлюза API. Что бы я хотел сделать, это проанализировать эту строку, чтобы получить данные, содержащиеся в Python 2.7. Я экспериментировал с классом cgi и использовал метод cgi.parse_multipart(), однако, я не может найти способ преобразования строки в требуемые аргументы. Любые советы?

Ответ 1

Комментарий: он прочный и совместимый со спецификацией?

Пока ваши данные следуют этим предварительным условиям:

  • Строка Первая - это граница
  • Следующий заголовок завершается пустой Линией
  • Каждая часть сообщения заканчивается символом

Комментарий. Что, если содержимое двоично, как поток JPEG?

Это похоже на разрыв, поскольку есть String Используемые методы и чтение содержимого использует .readline(), который зависит от Новая строка.
Поэтому для decode из BASE64, а затем unpack Multipart - это неправильный подход!


Комментарий: если версия, использующая общую библиотеку

Если вы можете предоставить свои данные как стандартные MIME Сообщение, вы можете использовать следующее:

import email
msg = email.message_from_string(mimeHeader+data)
print('is_multipart:{}'.format(msg.is_multipart()))

for part in msg.walk():
    if part.get_content_maintype() == 'multipart':
        continue

    filename = part.get_filename()
    payload = part.get_payload(decode=True)
    print('{} filename:{}\n{}'.format(part.get_content_type(), filename, payload))

Выход:

is_multipart:True
application/rtf filename:file1.rtf
b'{\rtf1\x07nsi\x07nsicpg1252\\cocoartf1504\\cocoasubrtf830\n{\x0conttbl\x0c0\x0cswiss\x0ccharset0'... (omitted for brevity)
application/rtf filename:file2.rtf
b'{\rtf1\x07nsi\x07nsicpg1252\\cocoartf1504\\cocoasubrtf830\n{\x0conttbl\x0c0\x0cswiss\x0ccharset0'... (omitted for brevity)

Вопрос: проанализируйте строку с несколькими данными

Pure Python Solution, например:

import re, io
with io.StringIO(data) as fh:
    parts = []
    part_line = []
    part_fname = None
    new_part = None
    robj = re.compile('.+filename=\"(.+)\"')

    while True:
        line = fh.readline()
        if not line: break

        if not new_part:
            new_part = line[:-1]

        if line.startswith(new_part):
            if part_line:
                parts.append({'filename':part_fname, 'content':''.join(part_line)})
                part_line = []

            while line and line != '\n':
                _match = robj.match(line)
                if _match: part_fname = _match.groups()[0]
                line = fh.readline()
        else:
            part_line.append(line)

for part in parts:
    print(part)

Выход

{'filename': 'file1.rtf', 'content': '{\rtf1\x07nsi\x07nsicpg1252\\cocoartf1504\\cocoasubrtf830\n... (omitted for brevity)
{'filename': 'file2.rtf', 'content': '{\rtf1\x07nsi\x07nsicpg1252\\cocoartf1504\\cocoasubrtf830\n... (omitted for brevity)

Протестировано с помощью Python: 3.4.2

Ответ 2

Если вы работаете с API, лучше использовать форматированные данные json. Вы можете использовать модуль запросов для отправки запроса PUT в API, и он вернет вам объект ответа, из которого вы можете легко получить json-данные, используя метод response.json()