Отправка файла и передача параметра с помощью pycurl

Я понял, как выполнять GET, PUT, DELETE и базовый POST с помощью pycurl.

Тем не менее, я не могу понять, что эквивалентно этой командной строке curl (которая отлично работает) для python:

curl -u admin:geoserver -XPOST -H 'Content-type: application/vnd.ogc.sld+xml' 
-d @/Users/rburhum/src/calthorpe/calthorpe/server/calthorpe/media/styles/1_my-scenario
"http://127.0.0.1:8080/geoserver/rest/styles?name=1_my-scenario" -v

Я видел образцы на репо. Тем не менее, передача одного параметра (в данном случае name) и файла для загрузки, похоже, не работает.

Для вызовов PUT я успешно использовал:

filesize = path.getsize(sldFile)
f = open(sldFile,'rb')

c = pycurl.Curl()

c.setopt(pycurl.HTTPHEADER, ["Content-type: application/vnd.ogc.sld+xml"])
c.setopt(pycurl.USERPWD, GEOSERVER_USER + ':' + GEOSERVER_PASSWORD)    
c.setopt(pycurl.INFILESIZE, filesize)
c.setopt(c.URL, str(GEOSERVER_URL + '/rest/styles/' + path.basename(sldFile)))
c.setopt(pycurl.PUT, 1)
c.setopt(pycurl.INFILE, f)
c.perform()
f.close()

таким образом, я наивно думал, что эквивалент POST будет:

filesize = path.getsize(sldFile)
f = open(sldFile,'rb')

c = pycurl.Curl()

c.setopt(c.URL, str(GEOSERVER_URL + '/rest/styles?name=' + path.basename(sldFile)))
c.setopt(pycurl.POST, 1)
c.setopt(pycurl.HTTPHEADER, ["Content-type: application/vnd.ogc.sld+xml"])
c.setopt(pycurl.USERPWD, GEOSERVER_USER + ':' + GEOSERVER_PASSWORD)    
c.setopt(pycurl.INFILESIZE, filesize)
c.setopt(pycurl.INFILE, f)
c.perform()
f.close()

Подробный вывод команды curl отображает следующее:

(calthorpe_env)[email protected] ~ / src / calthorpe / calthorpe / server / calthorpe $ calthorpe / server / calthorpe POST -H 'Content-type: application / vnd.ogc.sld + xml' -d @ / Users / rburhum / src / calthorpe / calthorpe / server / calthorpe / media / styles / 1_my-scene "http://127.0.0.1:8080/geoserver/rest/styles?name=1_my-scenario" -v * О подключении () к 127.0.0.1 порт 8080 (# 0) * Попытка 127.0.0.1 ... подключено * Подключено к 127.0.0.1 (127.0.0.1) порт 8080 (# 0) * Серверная аутентификация с использованием Basic с пользователем 'admin'

POST / geoserver / rest / styles? Name = 1_my-сценарий HTTP / 1.1 Авторизация: Базовый агент пользователя YWRtaW46Z2Vvc2VydmVy: curl / 7.21.4 (universal-apple-darwin11.0) libcurl / 7.21.4 OpenSSL / 0.9.8r zlib / 1.2.5 Хост: 127.0.0.1:8080 Принять: / Content-type: application / vnd.ogc.sld + xml Content-Length: 28135 Expect: 100-continue

> ‹HTTP / 1.1 100 Продолжить‹ HTTP / 1.1 201 Создано ‹Дата: Пн, 05 марта 2012 07:39:43 GMT‹ Местоположение: http://127.0.0.1:8080/geoserver/rest/styles/1_my-script‹ Сервер: Noelios-Restlet-Engine / 1.0..8 ‹Передача- Кодировка: фрагментировано ‹* Соединение №0 с хостом 127.0.0.1 осталось неизменным * Закрытие соединения №0

Я ясно вижу, что длина контента правильная.

Напротив, когда я делаю подробный вывод своего кода POST выше, я вижу, что длина содержимого равна -1 (и, следовательно, файл не передается, и сервер возвращает 500). Я издевался над переменной HTTPPOST, которая при определенных комбинациях обеспечивает правильную длину содержимого, но я все еще не могу заставить работать точный эквивалент приведенной выше команды CURL.


person rburhum    schedule 05.03.2012    source источник
comment
Я знаю, что это не то, что вы ищете, но если вы не слышали об этом, просмотрите запрашивает модуль.   -  person jterrace    schedule 05.03.2012
comment
@Tichodroma Я добавил больше контента   -  person rburhum    schedule 05.03.2012


Ответы (1)


Мне пришлось объединить несколько примеров, чтобы найти правильную комбинацию, и, конечно же, я воспользовался помощью некоторых хороших людей на канале IRC. Единственный способ заставить его работать - это сделать небольшую вариацию этого пример pycurl, который не работал так, как описано для меня.

class FileReader:
    def __init__(self, fp):
        self.fp = fp
    def read_callback(self, size):
        return self.fp.read(size)

затем позже я бы настроил вызов следующим образом:

c = pycurl.Curl()
c.setopt(c.URL, str(GEOSERVER_URL + '/rest/styles?name=' + path.basename(sldFile)))
c.setopt(pycurl.POST, 1)
filesize = path.getsize(sldFile)
f = open(sldFile,'rb')
c.setopt(pycurl.POSTFIELDSIZE, filesize)
c.setopt(pycurl.READFUNCTION, FileReader(f).read_callback)
c.setopt(pycurl.HTTPHEADER, ["Content-type: application/vnd.ogc.sld+xml"])
c.setopt(pycurl.USERPWD, GEOSERVER_USER + ':' + GEOSERVER_PASSWORD)

c.perform()
f.close()

Обратите внимание, что исходный образец не использовал POSTFIELDSIZE (имел только INFILESIZE), без него я не смог бы заставить его работать.

В качестве побочного примечания, в моем случае sldFile уже был slugify'ed, поэтому нет необходимости в urlencode, в противном случае вы можете захотеть это сделать.

person rburhum    schedule 05.03.2012