개발/Python

파이썬 POST 바이너리 데이터

MinorMan 2021. 4. 21. 10:13
반응형

<질문>

redmine과 인터페이스하기 위해 코드를 작성 중이며 프로세스의 일부로 일부 파일을 업로드해야하지만 바이너리 파일이 포함 된 Python에서 POST 요청을 수행하는 방법을 모르겠습니다.

나는 명령을 모방하려고here:

curl --data-binary "@image.png" -H "Content-Type: application/octet-stream" -X POST -u login:password http://redmine/uploads.xml

파이썬에서 (아래), 작동하지 않는 것 같습니다. 문제가 파일 인코딩과 관련이 있는지 또는 헤더에 문제가 있는지 확실하지 않습니다.

import urllib2, os

FilePath = "C:\somefolder\somefile.7z"
FileData = open(FilePath, "rb")
length = os.path.getsize(FilePath)

password_manager = urllib2.HTTPPasswordMgrWithDefaultRealm()
password_manager.add_password(None, 'http://redmine/', 'admin', 'admin')
auth_handler = urllib2.HTTPBasicAuthHandler(password_manager)
opener = urllib2.build_opener(auth_handler)
urllib2.install_opener(opener)
request = urllib2.Request( r'http://redmine/uploads.xml', FileData)
request.add_header('Content-Length', '%d' % length)
request.add_header('Content-Type', 'application/octet-stream')
try:
    response = urllib2.urlopen( request)
    print response.read()
except urllib2.HTTPError as e:
    error_message = e.read()
    print error_message

서버에 액세스 할 수 있으며 인코딩 오류처럼 보입니다.

...
invalid byte sequence in UTF-8
Line: 1
Position: 624
Last 80 unconsumed characters:
7z¼¯'ÅÐз2^Ôøë4g¸R

<답변1>

기본적으로 당신이하는 일은 옳습니다. 링크 된 redmine 문서를 보면 URL의 점 뒤의 접미사가 게시 된 데이터 유형 (JSON의 경우 .json, XML의 경우 .xml)을 나타내는 것으로 보입니다.Processing by AttachmentsController#upload as XML. 문서에 버그가있을 수 있고 바이너리 데이터를 게시하려면 사용을 시도해야합니다.http://redmine/uploads대신 URLhttp://redmine/uploads.xml.

Btw, 나는 매우 좋고 매우 인기가 좋습니다.RequestsPython에서 http 용 라이브러리. 표준 lib (urllib2)에있는 것보다 훨씬 낫습니다. 인증도 지원하지만 여기서는 간결하게 건너 뛰었습니다.

import requests
with open('./x.png', 'rb') as f:
    data = f.read()
res = requests.post(url='http://httpbin.org/post',
                    data=data,
                    headers={'Content-Type': 'application/octet-stream'})

# let's check if what we sent is what we intended to send...
import json
import base64

assert base64.b64decode(res.json()['data'][len('data:application/octet-stream;base64,'):]) == data

최신 정보

이것이 Requests에서는 작동하지만 urllib2에서는 작동하지 않는 이유를 알아 보려면 전송되는 내용의 차이를 조사해야합니다. 이를 확인하기 위해 포트 8888에서 실행되는 http 프록시 (Fiddler)로 트래픽을 보냅니다.

요청 사용

import requests

data = 'test data'
res = requests.post(url='http://localhost:8888',
                    data=data,
                    headers={'Content-Type': 'application/octet-stream'})

우리는보다

POST http://localhost:8888/ HTTP/1.1
Host: localhost:8888
Content-Length: 9
Content-Type: application/octet-stream
Accept-Encoding: gzip, deflate, compress
Accept: */*
User-Agent: python-requests/1.0.4 CPython/2.7.3 Windows/Vista

test data

및 urllib2 사용

import urllib2

data = 'test data'    
req = urllib2.Request('http://localhost:8888', data)
req.add_header('Content-Length', '%d' % len(data))
req.add_header('Content-Type', 'application/octet-stream')
res = urllib2.urlopen(req)

우리는 얻는다

POST http://localhost:8888/ HTTP/1.1
Accept-Encoding: identity
Content-Length: 9
Host: localhost:8888
Content-Type: application/octet-stream
Connection: close
User-Agent: Python-urllib/2.7

test data

나는 당신이 관찰하는 다른 행동을 보증하는 어떤 차이도 보지 못했습니다. http 서버가 검사하는 것이 드물지 않다고 말하면서User-Agent헤더 및 값에 따라 동작을 변경합니다. Requests에서 보낸 헤더를 하나씩 변경하여 urllib2에서 보낸 헤더와 동일하게 만들고 작동이 중지되면 확인하십시오.


<답변2>

이것은 잘못된 업로드와는 관련이 없습니다. HTTP 오류는 401 권한이 없음을 명확하게 지정하고 CSRF 토큰이 유효하지 않음을 알려줍니다. 업로드와 함께 유효한 CSRF 토큰을 보내십시오.

여기에서 csrf 토큰에 대해 자세히 알아보십시오.

CSRF 토큰이란 무엇입니까? 그 중요성은 무엇이며 어떻게 작동합니까?


<답변3>

다음과 같이 Content-Disposition 헤더를 추가해야합니다 (여기서는 mod-python을 사용했지만 원칙은 동일해야합니다).

request.headers_out['Content-Disposition'] = 'attachment; filename=%s' % myfname

<답변4>

당신이 사용할 수있는unirest, 요청을 쉽게 게시 할 수있는 방법을 제공합니다. `

import unirest
 
def callback(response):
 print "code:"+ str(response.code)
 print "******************"
 print "headers:"+ str(response.headers)
 print "******************"
 print "body:"+ str(response.body)
 print "******************"
 print "raw_body:"+ str(response.raw_body)
 
# consume async post request
def consumePOSTRequestASync():
 params = {'test1':'param1','test2':'param2'}
 
 # we need to pass a dummy variable which is open method
 # actually unirest does not provide variable to shift between
 # application-x-www-form-urlencoded and
 # multipart/form-data
  
 params['dummy'] = open('dummy.txt', 'r')
 url = 'http://httpbin.org/post'
 headers = {"Accept": "application/json"}
 # call get service with headers and params
 unirest.post(url, headers = headers,params = params, callback = callback)
 
 
# post async request multipart/form-data
consumePOSTRequestASync()
반응형