Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to upload file as array element with poster?

I am trying to repeat the following curl request:

curl -i -k -H "Content-Type: multipart/form-data" \
  -F "method=uploadphoto" \
  -F "version=1.2.3" \
  -F "format=json" \
  -F "image[0]=@/Users/user/Downloads/file.jpeg" \
  https://example.com/api

with poster usage:

from poster.encode import multipart_encode, MultipartParam

file_content = open('/Users/user/Downloads/file.jpeg', 'rb')
url = 'https://example.com/api'
headers = {
    'Content-Type': 'multipart/form-data'
}
parms = {
    'method': 'uploadphoto',
    'version': '1.2.3',
    'format': 'json'
}
mp_parms = []
for name, value in parms.items():
    mp_parms.append(MultipartParam(name, value, filetype='text/plain'))
file_photo = MultipartParam('image', file_content, 'file.jpeg', 'application/image')
mp_parms.append(file_photo)
parameters, headers = multipart_encode(mp_parms)
response = urlfetch.Fetch(url, payload=''.join(parameters), headers=headers, method=urlfetch.POST, deadline=60)

But looks like image field is passed as single field, when array should be passed. Usage of image[0] as name doesn't help.

How can I fix it?

like image 903
LA_ Avatar asked Jun 28 '17 18:06

LA_


1 Answers

I switched the example to use requests because I don't have the Google App SDK installed but the below code works.

One important change was changing image to image[] to make so the remote end would recognize multiple inputs with the same name. Not sure if the same would apply with urlfetch but I had to add an extra \r\n after the raw post body.

For illustration and testing, I had it upload a second image.

First, the code:

from poster.encode import multipart_encode, MultipartParam
import requests
from pprint import pprint

file_content = open('/home/me/Pictures/admin.jpg', 'rb').read()
file_content2 = open('/home/me/Pictures/ed.jpeg', 'rb').read()
url = 'http://localhost/test.php'
parms = {
    'method': 'uploadphoto',
    'version': '1.2.3',
    'format': 'json'
}
mp_parms = []
for name, value in parms.items():
    mp_parms.append(MultipartParam(name, value, filetype='text/plain'))

file_photo = MultipartParam('image[]', file_content, 'file.jpeg', 'image/jpg')
mp_parms.append(file_photo)

file_photo = MultipartParam('image[]', file_content2, 'test.jpeg', 'image/jpg')
mp_parms.append(file_photo)

parameters, headers = multipart_encode(mp_parms)

pprint(headers)

#response = urlfetch.Fetch(url, payload=''.join(parameters), headers=headers, method=urlfetch.POST, deadline=60)

r = requests.post(url, data=''.join(parameters) + "\r\n", headers=headers)

print r.text

The test.php script it posts to contains <?php var_dump($_POST, $_FILES, $_SERVER); and shows output like:

// The post data:
array(3) {
  ["version"]=>
  string(5) "1.2.3"
  ["method"]=>
  string(11) "uploadphoto"
  ["format"]=>
  string(4) "json"
}

// The uploaded files (both of them as expected):
array(1) {
  ["image"]=>
  array(5) {
    ["name"]=>
    array(2) {
      [0]=>
      string(9) "file.jpeg"
      [1]=>
      string(9) "test.jpeg"
    }
    ["type"]=>
    array(2) {
      [0]=>
      string(9) "image/jpg"
      [1]=>
      string(9) "image/jpg"
    }
    ["tmp_name"]=>
    array(2) {
      [0]=>
      string(14) "/tmp/phpnbpGGx"
      [1]=>
      string(14) "/tmp/php7TVcyL"
    }
    ["error"]=>
    array(2) {
      [0]=>
      int(0)
      [1]=>
      int(0)
    }
    ["size"]=>
    array(2) {
      [0]=>
      int(71066)
      [1]=>
      int(30450)
    }
  }
}

# some server vars:
["CONTENT_LENGTH"]=>
  string(6) "102186"
["CONTENT_TYPE"]=>
  string(62) "multipart/form-data; boundary=73772149e2ef4a5daf9b5eb18a5d73f5"

It produced the following POST as grabbed in Wireshark:

POST /test.php HTTP/1.1
Host: localhost
Connection: keep-alive
Accept-Encoding: gzip, deflate
Accept: */*
User-Agent: python-requests/2.12.1
Content-Length: 102186
Content-Type: multipart/form-data; boundary=176473ffab3146b5bfffc6185ad9474a

--176473ffab3146b5bfffc6185ad9474a
Content-Disposition: form-data; name="version"
Content-Type: text/plain

1.2.3
--176473ffab3146b5bfffc6185ad9474a
Content-Disposition: form-data; name="method"
Content-Type: text/plain

uploadphoto
--176473ffab3146b5bfffc6185ad9474a
Content-Disposition: form-data; name="format"
Content-Type: text/plain

json
--176473ffab3146b5bfffc6185ad9474a
Content-Disposition: form-data; name="image[]"; filename="file.jpeg"
Content-Type: image/jpg

......JFIF.....H.H.....C...................................
...snip...
..K5{......1....fh........k..n...
--176473ffab3146b5bfffc6185ad9474a
Content-Disposition: form-data; name="image[]"; filename="test.jpeg"
Content-Type: image/jpg

......JFIF.............;CREATOR: gd-jpeg v1.0 (using IJG JPEG v80), quality = 90
...snip...
6.......~.h.j......}29..b~.h.).2.!E..tU.........L....d...lv..+....f)Y...
--176473ffab3146b5bfffc6185ad9474a--

HTTP/1.1 200 OK

I hope that helps. I'm not sure if the API will care, but you might want to base64 encode the image data and set the Content-Encoding to base64 to keep raw binary out of the post.

If you're still stuck after integrating some of the changes let me know!

like image 129
drew010 Avatar answered Oct 20 '22 02:10

drew010