I am trying to use Playwright to PUT multipart form-data with the Python API. Here is the relevant documentation.
However, it's not clear how to structure the multipart argument. The docs say:
multipart: Dict[str, str|float|bool|[ReadStream]|Dict] (optional) name: str File name mimeType: str File type buffer: bytes File contentProvides an object that will be serialized as html form using multipart/form-data encoding and sent as this request body. If this parameter is specified content-type header will be set to multipart/form-data unless explicitly provided. File values can be passed either as fs.ReadStream or as file-like object containing file name, mime-type and its content.
So, we pass it a dict with str keys, and either str, float, bool, ReadStream or Dict values. But what keys should we use? Also, how do I pass an fs.ReadStream, which is a JavaScript object, from a Python script? It looks like the keys should be 'name', 'mimeType' and 'buffer'. However, checking the generated HTTP request with nc -l -p 8080 shows that a new form part is created for each key in the multipart dictionary.
I'm essentially trying to replicate the following curl command:
curl 'localhost:8080' -X PUT --form file='@test.zip;filename=test.zip;type=application/x-zip-compressed'
Assuming that you have previously run nc -l -p 8080 | less, you will see the following HTTP request:
PUT / HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.81.0
Accept: */*
Content-Length: 380
Content-Type: multipart/form-data; boundary=------------------------f3dde3ddff901cd3
--------------------------f3dde3ddff901cd3
Content-Disposition: form-data; name="file"; filename="test.zip"
Content-Type: application/x-zip-compressed
PK^C^D
^@^@^@^@^@<D1>`xW^N<93>ESC<91>
^@^@^@
^@^@^@^H^@^\^@test.txtUT ^@^C^Z<F7>_e^Z<F7>_eux^K^@^A^D<E8>^C^@^@^D<E8>^C^@^@Test file
PK^A^B^^^C
^@^@^@^@^@<D1>`xW^N<93>ESC<91>
^@^@^@
^@^@^@^H^@^X^@^@^@^@^@^A^@^@^@<FF><81>^@^@^@^@test.txtUT^E^@^C^Z<F7>_eux^K^@^A^D<E8>^C^@^@^D<E8>^C^@^@PK^E^F^@^@^@^@^A^@^A^@N^@^@^@L^@^@^@^@^@
--------------------------f3dde3ddff901cd3--
Here is a failed attempt to achieve an equivalent HTTP request with Playwright:
from playwright.sync_api import sync_playwright
with sync_playwright() as playwright:
browser = playwright.chromium.launch()
context = browser.new_context()
page = context.new_page()
upload_file = "test.zip"
with open(upload_file, "rb") as file:
response = page.request.put(
"http://localhost:8080",
multipart={
"name": "file",
"filename": upload_file,
"mimeType": "application/x-zip-compressed",
"buffer": file.read(),
}
)
print(response)
This produces the following (clearly wrong) HTTP request:
PUT / HTTP/1.1
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/115.0.5790.24 Safari/537.36
accept: */*
accept-encoding: gzip,deflate,br
content-type: multipart/form-data; boundary=----WebKitFormBoundarynU6IBe6rgn1KUgpw
content-length: 365
Host: localhost:8080
Connection: close
------WebKitFormBoundarynU6IBe6rgn1KUgpw
content-disposition: form-data; name="name"
file
------WebKitFormBoundarynU6IBe6rgn1KUgpw
content-disposition: form-data; name="filename"
test.zip
------WebKitFormBoundarynU6IBe6rgn1KUgpw
content-disposition: form-data; name="mimeType"
application/x-zip-compressed
------WebKitFormBoundarynU6IBe6rgn1KUgpw--
How can I replicate the curl HTTP request with Playwright?