I am writing a file uploader on Selectel. Simplified code looks like this:

filename = 'example_watermarked.jpg' headers['Content-Length'] = str(os.path.getsize(filename)) files = { 'file' : open(filename, 'rb') } url = auth['url'] + 'images/python_uploader/' + filename response = requests.put(url, files=files, headers=headers) 

It turns out the next thing - the source file is 38158 bytes. File on selector - 38317 bytes. The file, of course, does not open. Where does the "extra" 159 bytes come from? What am I doing wrong?

PS os.path.getsize(filename) returns 38158, i.e. correct size.

PPS Through Postman I tried, in the same way, I downloaded a beaten file. Only now it is 38355 bytes.

Update: The following block was added to the beginning:

 --de966cbba4ab406da3dd088e63bd9c7f Content-Disposition: form-data; name="file"; filename="example_watermarked.jpg" 

In the end - here is this:

 --de966cbba4ab406da3dd088e63bd9c7f-- 

The rest is binary data.

  • Try not to specify the Content-Length - requests will install it himself, besides, the file size on the disk may differ from its actual size - gil9red
  • @ gil9red I tried initially, the same. Then I clearly indicated - Captain Flint
  • If possible, try using the browser to repeat the operation and see what is being sent. Is there an example? those. a link to which you could send a file and check for yourself what works - gil9red
  • @ gil9red nuuuu ... Theoretically, on my website I can write a route that will receive the file, and see what comes along. However, Wang, that Selectle does not mow, because from a nearby computer, a colleague loads through PHP normally - Captain Flint
  • Well, I do not know, a finger to the sky: 1. and through post you can send files? 2. and what version of python / requests? 3. Try to enable requests and see the request: github.com/gil9red/SimplePyScripts/blob/… - gil9red

2 answers 2

I wrote to tech support, that's what they said to me:

Please try to transfer in the request not the object itself, but its body. Code example:

 import requests headers = {"X-Auth-User": "0000", "X-Auth-Key": "xxxxxxxxx"} r = requests.get("https://auth.selcdn.ru/", headers=headers) assert r.status_code is 204 auth_token = r.headers.get("X-Auth-Token") storage_url = r.headers.get("X-Storage-Url") filename = "sample-pic-4.jpg" data = open(filename, "rb").read() r = requests.put(storage_url + "testcontainer/testupload/" + filename, data=data, headers={"X-Auth-Token": auth_token}) r.raise_for_status() 

I rewrote my code taking into account their corrections, it all worked

  • That is, if we want to load a file of several gigabytes into a container, then you first need to load it into memory in any case, and then into the container. - insolor
  • @insolor fortunately, such a task is not worth it yet. If I do, I'll write to them again :) - Captain Flint

It all comes down to the fact that the Selectel API does not properly handle files sent by the POST method with Content-Type: multipart/form-data , RFC 7578 .

See here for example: Generating HTTP requests, POST method , sample request from this article:

 POST http://www.site.ru/postnews.html HTTP/1.0\r\n Host: www.site.ru\r\n Referer: http://www.site.ru/news.html\r\n Cookie: income=1\r\n Content-Type: multipart/form-data; boundary=1BEF0A57BE110FD467A\r\n Content-Length: 491\r\n \r\n --1BEF0A57BE110FD467A\r\n Content-Disposition: form-data; name="news_header"\r\n \r\n Пример новости\r\n --1BEF0A57BE110FD467A\r\n Content-Disposition: form-data; name="news_file"; filename="news.txt"\r\n Content-Type: application/octet-stream\r\n Content-Transfer-Encoding: binary\r\n \r\n А вот такая новость, которая лежит в файле news.txt\r\n --1BEF0A57BE110FD467A--\r\n 

In your question, de966cbba4ab406da3dd088e63bd9c7f is a boundary, i.e. label indicating the boundaries of the data block. When receiving the start tag, the service data after it before the double line break and the end tag should be cut off.

The lack of support for this variant of the request, as I understand it, will lead to the fact that before sending the file will have to be completely loaded into memory, and if this file "weighs" several gigabytes, then this already becomes a problem.