如何使用请求POST方法上传PDF文件,并且请求必须使用python for xerAPI格式化为多部分MIME



我正试图使用python请求库(POST方法(和Xeros FilesAPI在Xero帐户中上传PDF文件;请求必须格式化为多部分MIME";并有一些必填字段(链接(,但我不知道如何做到这一点。。。如果我请求GET,我会得到Xero帐户中的文件列表,但在发布文件时遇到问题(POST请求(。。。

我的代码:

post_url = 'https://api.xero.com/files.xro/1.0/Files/'
files = {'file': open('/home/mobin/PycharmProjects/s3toxero/data/in_test_upload.pdf', 'rb')}
response = requests.post(
post_url,
headers={
'Authorization': 'Bearer ' + new_tokens[0],
'Xero-tenant-id': xero_tenant_id,
'Accept': 'application/json',
'Content-type': 'multipart/form-data; boundary=JLQPFBPUP0',
'Content-Length': '1068',
},
files=files,
)
json_response = response.json()
print(f'Uploading Responsoe ==> {json_response}')
print(f'Uploading Responsoe ==> {response}')

错误台架/响应:

Uploading Responsoe ==> [{'type': 'Validation', 'title': 'Validation failure', 'detail': 'No file is was attached'}]
Uploading Responsoe ==> <Response [400]>

正如我所看到的,您设置的边界不正确。您在标题中设置了它,但没有告诉requests库使用自定义边界。让我给你看一个例子:

>>> import requests
>>> post_url = 'https://api.xero.com/files.xro/1.0/Files/'
>>> files = {'file': open('/tmp/test.txt', 'rb')}
>>> headers = {
...    'Authorization': 'Bearer secret',
...    'Xero-tenant-id': '42',
...    'Accept': 'application/json',
...    'Content-type': 'multipart/form-data; boundary=JLQPFBPUP0',
...    'Content-Length': '1068',
... }
>>> print(requests.Request('POST', post_url, files=files, headers=headers).prepare().body.decode('utf8'))
--f3e21ca5e554dd96430f07bb7a0d0e77
Content-Disposition: form-data; name="file"; filename="test.txt"

--f3e21ca5e554dd96430f07bb7a0d0e77--

正如您所看到的,实际边界(f3e21ca5e554dd96430f07bb7a0d0e77(与标头(JLQPFBPUP0(中传递的边界不同。

实际上,您可以直接使用请求模块来控制边界,如下所示:

让我们准备一个测试文件:

$ touch /tmp/test.txt
$ echo 'Hello, World!' > /tmp/test.txt 

测试:

>>> import requests
>>> post_url = 'https://api.xero.com/files.xro/1.0/Files/'
>>> files = {'file': open('/tmp/test.txt', 'rb')}
>>> headers = {
...     'Authorization': 'Bearer secret',
...     'Xero-tenant-id': '42',
...     'Accept': 'application/json',
...     'Content-Length': '1068',
... }
>>> body, content_type = requests.models.RequestEncodingMixin._encode_files(files, {})
>>> headers['Content-type'] = content_type
>>> print(requests.Request('POST', post_url, data=body, headers=headers).prepare().body.decode('utf8'))
--db57d23ff5dee7dc8dbab418e4bcb6dc
Content-Disposition: form-data; name="file"; filename="test.txt"
Hello, World!
--db57d23ff5dee7dc8dbab418e4bcb6dc--
>>> headers['Content-type']
'multipart/form-data; boundary=db57d23ff5dee7dc8dbab418e4bcb6dc'

此处的边界与标头中的边界相同。

另一种替代方案是使用CCD_ 4;下面的例子取自这个GitHub问题线程:

from requests_toolbelt import MultipartEncoder
fields = {
# your multipart form fields
}
m = MultipartEncoder(fields, boundary='my_super_custom_header')
r = requests.post(url, headers={'Content-Type': m.content_type}, data=m.to_string())

但最好不要手动传递bundary,并将此工作委托给请求库。


更新:

使用Xero Files API和Python请求的最小工作示例:

from os.path import abspath
import requests
access_token = 'secret'
tenant_id = 'secret'
filename = abspath('./example.png')
post_url = 'https://api.xero.com/files.xro/1.0/Files'
files = {'filename': open(filename, 'rb')}
values = {'name': 'Xero'}
headers = {
'Authorization': f'Bearer {access_token}',
'Xero-tenant-id': f'{tenant_id}',
'Accept': 'application/json',
}
response = requests.post(
post_url,
headers=headers,
files=files,
data=values
)
assert response.status_code == 201

我已经用Xero的Files API测试了这一点,以上传一个名为;helloworld.rtf";在与我的主app.py文件相同的目录中。

var1 = "Bearer "
var2 = YOUR_ACCESS_TOKEN
access_token_header = var1 + var2
body = open('helloworld.rtf', 'rb')
mp_encoder = MultipartEncoder(
fields={
'helloworld.rtf': ('helloworld.rtf', body),
}
)
r = requests.post(
'https://api.xero.com/files.xro/1.0/Files',
data=mp_encoder,  # The MultipartEncoder is posted as data
# The MultipartEncoder provides the content-type header with the boundary:
headers={
'Content-Type': mp_encoder.content_type,
'xero-tenant-id': YOUR_XERO_TENANT_ID,
'Authorization': access_token_header
}
)

看起来问题已经解决了。供将来使用Xero支持包的开发人员参考(https://github.com/XeroAPI/xero-python)

我们刚刚将files_api示例代码添加到示例应用程序中,因此如果您使用Python SDK ,下面的代码将上传一个文件

https://github.com/XeroAPI/xero-python-oauth2-app/pull/29/files

name = "my-image"
filename= "my-image.jpg"
mime_type = "image/jpg"
with open('my-image.jpg', 'rb') as f:
body = f.read()
try:
file_object = files_api.upload_file(
xero_tenant_id, 
name = name, 
filename= filename, 
mime_type = mime_type,
body=body
)
except AccountingBadRequestException as exception:
json = jsonify(exception.error_data)
else:
json = serialize_model(file_object)

最新更新