zoukankan      html  css  js  c++  java
  • Python requests-toolbelt库 生成文件上传的multipart/form-data格式数据

    需求背景

      想使用requests做一个自动上传的功能,发现这里问题挺多的,比如直接发POST包,或者直接data=二进制流,都会上传失败。我觉得应该挺多人会遇到这个问题,就记录一下。

    如上图上传功能,一般分为input标签,非input标签。我这里也不管什么标签,直接抓包看数据流。

    Content-Type为传输内容的类型,一般有如下几种:

    • application/x-www-form-urlencoded:默认的编码方式。 在最早的http post请求中,只支持application/x-www-form-urlencoded,参数都是通过浏览器的url传递。其实是不支持文件上传的,这样有很多不便。

    • multipart/form-data:用于支持向服务器发送二进制数据,指定传输数据为二进制类型,比如图片、mp3、文件。

    • text/plain:纯文体的传输。空格转换为 “+” 加号,但不对特殊字符编码。

    • application/json 等

    还有好多类型,建议查谷哥。了解到我们现在要上传multipart/form-data类型的数据,那么我们看看他的格式结构如何。


    multipart/form-data 格式结构解析

    格式如下:

    POST /xxxxxxx/upload/ HTTP/1.1
    Host: xxxxx.xxxxxxxxxxxx.cn
    User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.9 Safari/537.36
    Accept: */*
    Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
    Accept-Encoding: gzip, deflate
    Content-Type: multipart/form-data; boundary=---------------------------2385610611750
    Content-Length: 1616477
    Connection: close
    Referer: http://xxxxxx.xxxxxxx.cn/
    
    -----------------------------2385610611750
    Content-Disposition: form-data; name="id"
    
    WU_FILE_0
    -----------------------------2385610611750
    Content-Disposition: form-data; name="name"
    
    app-debug.apk
    -----------------------------2385610611750
    Content-Disposition: form-data; name="type"
    
    application/octet-stream
    -----------------------------2385610611750
    Content-Disposition: form-data; name="lastModifiedDate"
    
    2019/10/16 下午8:18:58
    -----------------------------2385610611750
    Content-Disposition: form-data; name="size"
    
    1615720
    -----------------------------2385610611750
    Content-Disposition: form-data; name="file"; filename="app-debug.apk"
    Content-Type: application/octet-stream
    
    PKxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx(二进制数据流,很长...)
    -----------------------------2385610611750--
    

    格式大致分为如下几种模块:

    • 分隔符
    • 单 Key = Value 键值对
    • 多 Key = Value 键值对
    • 结束符

    相信你看完我上面那么用心的截图,很容易就看懂它这种分片格式了,懂了格式就好构造了。


    使用requests-toolbelt的MultipartEncoder 构造

    python-requests是一个实用程序的集合,感觉基本就是用于辅助requests,最常用的功能就是使用MultipartEncoder构造上面说的这种multipart/form-data类型的数据。

    官网:https://pypi.org/project/requests-toolbelt/

    Demo:

    import requests
    from requests_toolbelt.multipart.encoder import MultipartEncoder
    
    def up():
        url= 'xxx';
        headers={x:x};
    
        multipart_encoder = MultipartEncoder(
            fields={
                "id": "WU_FILE_0",
                "name": "app-debug.apk",
                "type": "application/octet-stream",
                "lastModifiedDate": "2019/10/16",
                "filename": "app-debug.apk",
                "Content-Type": "application/octet-stream",
                "file": (
                "app-debug.apk", open('D:\xxxx.apk', 'rb'), 'application/octet-stream')
            },
            boundary='-----------------------------2385610611750'
        )
    
        result = requests.post(url, headers=headers, data=multipart_encoder)
    

    boundary放置分隔符,结束符好像会根据这个分隔符自动生成。
    单K=V形式都很简单,一看就懂,多K=V形式的就按照他的格式配就好:"file": ("app-debug.apk", open('D:\xxxx.apk', 'rb'), 'application/octet-stream').


    关于boundary的作用

    boundary参考:https://stackoverflow.com/questions/3508338/what-is-the-boundary-in-multipart-form-data

    boundary示例如下图:

    这个boundary的作用就是类似URL提交参数时(www.baidu.com?id=22&name=lisi),作用和&是一模一样的,只不过这个分割的是浏览器自定义(随机生成)的,一般为字母或数字。只要这个分割符不和参数里面的值有一模一样就不有什么问题,服务器接收到整个数据包的时候,只是通过这个分隔符来分割参数,不会去校验这个boundary,只会去校验里面参数值是否符合条件。

    Content-Type: multipart/form-data; boundary=--133951685715237
    Content-Length: 1617044
    Origin: https://www.testin.net
    Connection: close
    Referer: https://www.testin.net/app/search-list.htm
    
    ----133951685715237
    Content-Disposition: form-data; name="id"
    
    182832
    ----133951685715237
    Content-Disposition: form-data; name="fileMd5"
    
    35403faf30dc7b90354945c789d649f7
    ----133951685715237
    Content-Disposition: form-data; name="sign"
    
    WU_FILE_0
    ----133951685715237--
    

    格式如上,boundary=--133951685715237,只有两个--,参数分割的时候----133951685715237,有四个----,最后标记结束符的时候还需要多两个--,----133951685715237--。

    如果是使用requests_toolbelt的话,只需要设置和Content-Type: multipart/form-data; boundary=--133951685715237boundary=xxx即可,因为它会自动帮助你调整格式。


    参考文章

    https://www.jianshu.com/p/0023bb7afddb

    https://blog.csdn.net/xuezhangjun0121/article/details/82023320

    https://blog.csdn.net/liyingke112/article/details/70233776

    https://my.oschina.net/lykops/blog/1506911

    https://stackoverflow.com/questions/3508338/what-is-the-boundary-in-multipart-form-data

  • 相关阅读:
    poj 1265 Area(Pick定理)
    poj 2954 Triangle(Pick定理)
    poj 1654 Area(多边形面积)
    bzoj 1069 [SCOI2007]最大土地面积(旋转卡壳)
    227 用栈模拟汉诺塔问题
    213 字符串压缩
    212 空格替换
    211 字符串置换
    209 First Unique Character in a String
    197 排列序号
  • 原文地址:https://www.cnblogs.com/mysticbinary/p/11692909.html
Copyright © 2011-2022 走看看