How to send a “multipart/related” with requests in python?

后端 未结 2 1750
梦谈多话
梦谈多话 2021-01-01 23:20

I\'m trying to send a multipart/related message using requests in Python. The script seems simple enough, except that requests only seems to allow multipart/form-data messag

相关标签:
2条回答
  • 2021-01-02 00:04

    You'll have to create the MIME encoding yourself. You can do so with the email.mime package:

    import requests
    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText
    
    related = MIMEMultipart('related')
    
    submission = MIMEText('text', 'xml', 'utf8')
    submission.set_payload(open('submission_set.xml', 'rb').read())
    related.attach(submission)
    
    document = MIMEText('text', 'plain')
    document.set_payload(open('document.txt', 'rb').read())
    related.attach(document)
    
    body = related.as_string().split('\n\n', 1)[1]
    headers = dict(related.items())
    
    r = requests.post(url, data=body, headers=headers)
    

    I presumed the XML file uses UTF-8, you probably want to set a character set for the document entry as well.

    requests only knows how to create multipart/form-data post bodies; the multipart/related is not commonly used.

    0 讨论(0)
  • 2021-01-02 00:04

    I'm working with requests and the Google Drive API "Multipart" upload.

    The email.mime solution did not work with Google's API, so I dug into the requests source code to see how it implements multipart/form-data bodies.

    requests uses the urllib3.filepost.encode_multipart_formdata() helper, which can be wrapped to provide multipart/related:

    from urllib3.filepost import encode_multipart_formdata, choose_boundary
    
    def encode_multipart_related(fields, boundary=None):
        if boundary is None:
            boundary = choose_boundary()
    
        body, _ = encode_multipart_formdata(fields, boundary)
        content_type = str('multipart/related; boundary=%s' % boundary)
    
        return body, content_type
    

    Now we can use encode_multipart_related() to create a (body, content_type) tuple that matches Google's requirements:

    import json
    from urllib3.fields import RequestField
    
    def encode_media_related(metadata, media, media_content_type):
        rf1 = RequestField(
            name='placeholder',
            data=json.dumps(metadata),
            headers={'Content-Type': 'application/json; charset=UTF-8'},
        )
        rf2 = RequestField(
            name='placeholder2',
            data=media,
            headers={'Content-Type': media_content_type},
        )
        return encode_multipart_related([rf1, rf2])
    

    Here is a full example that uses our encode_media_related() to upload a hello world file to Google Drive, using the google_auth library.

    from google.oauth2 import service_account
    import google.auth.transport.requests
    
    credentials = service_account.Credentials.from_service_account_file(
        PATH_TO_SERVICE_FILE,
        scopes=['https://www.googleapis.com/auth/drive.file'],
    )
    session = google.auth.transport.requests.AuthorizedSession(credentials)
    
    metadata = {
        'mimeType': 'application/vnd.google-apps.document',
        'name': 'Test Upload',
    }
    body, content_type = encode_media_related(
        metadata,
        '<html><body><p>Hello World!</body></html>',
        'text/html; charset=UTF-8',
    )
    resp = session.post(
        'https://www.googleapis.com/upload/drive/v3/files',
        data=body,
        params={'uploadType': 'multipart'},
        headers={'Content-Type': content_type},
    )
    
    print 'Uploaded to file with id: %s' % resp.json()['id']
    
    0 讨论(0)
提交回复
热议问题