2013-10-18 84 views
19

我需要做一个API调用,以及一个带有JSON字符串的文件以及关于该文件的详细信息。Python请求:在单个请求中发布JSON和文件

我试图使用Python请求LIB做到这一点:

import requests 

info = { 
    'var1' : 'this', 
    'var2' : 'that', 
} 

data = json.dumps({ 
    'token' : auth_token, 
    'info' : info, 
}) 

headers = {'Content-type': 'multipart/form-data'} 

files = {'document': open('file_name.pdf', 'rb')} 

r = requests.post(url, files=files, data=data, headers=headers) 

这引发以下错误:

raise ValueError("Data must not be a string.") 
ValueError: Data must not be a string 

如果我删除请求的“文件”,它的工作原理。
如果我从请求中删除'数据',它就会起作用。
如果我不将数据编码为JSON,它就可以工作。

由于这个原因,我认为错误是与在同一请求中发送JSON数据和文件有关。

有关如何使这项工作的任何想法?

+0

似乎有一个错字在你的代码中:'var2'后面应该跟一个''',对吧? –

+0

是的,修正了我的例子,谢谢! – oznu

回答

13

使用JSON不编码。

import requests 

info = { 
    'var1' : 'this', 
    'var2' : 'that', 
} 

data = { 
    'token' : auth_token, 
    'info' : info, 
} 

headers = {'Content-type': 'multipart/form-data'} 

files = {'document': open('file_name.pdf', 'rb')} 

r = requests.post(url, files=files, data=data, headers=headers) 

请注意,这可能不一定是你想要的,因为它将成为另一个表单数据部分。

+0

如果我按照你的建议去做,我会得到另一个例外:“需要超过1个值才能解包”,并想知道该怎么做:-( – Arkady

1

我不认为你可以发送一个多编码文件数据和文件,所以你需要让你的数据的“文件”太:

files = { 
    'data' : data, 
    'document': open('file_name.pdf', 'rb') 
} 

r = requests.post(url, files=files, headers=headers) 
+0

你会如何解码?客户端会得到一个不是JSON的python字典?这是一个问题! – ivansabik

+0

@sabik:请求将字典编码为表单数据。 – RemcoGerlich

5

看到这个线程How to send JSON as part of multipart POST-request

不要设置Content-Type头自己,留给pyrequests产生

def send_request(): 
payload = {"param_1": "value_1", "param_2": "value_2"} 
files = { 
    'json': (None, json.dumps(payload), 'application/json'), 
    'file': (os.path.basename(file), open(file, 'rb'), 'application/octet-stream') 
} 

r = requests.post(url, files=files) 
print(r.content) 
+0

哇......'没有'救了我!!! – EvgenyKolyakov

+0

让我们说烧瓶是接收器是什么将代码烧瓶的方式呢? – MouIdri

+0

@Mouldri尝试'response.data' –

0

更重要的是:

files = { 
    'document': open('file_name.pdf', 'rb') 
} 

这只有在你的文件位于你的脚本所在的同一目录时才有效t是。

如果要附加来自不同目录下的文件,你应该做的:

files = { 
    'document': open(os.path.join(dir_path, 'file_name.pdf'), 'rb') 
} 

dir_path与您“file_name.pdf”文件的目录。

但是,如果您想发送多个PDF文件呢?

您可以简单地创建一个自定义函数来返回您需要的文件列表(在您的情况下,只能使用.pdf扩展名)。这也包括在子目录中的文件(搜索文件递归):

def prepare_pdfs(): 
    return sorted([os.path.join(root, filename) for root, dirnames, filenames in os.walk(dir_path) for filename in filenames if filename.endswith('.pdf')]) 

然后,你可以把它叫做:

my_data = prepare_pdfs() 

而且具有简单的循环:

for file in my_data: 

    pdf = open(file, 'rb') 

    files = { 
     'document': pdf 
    } 

    r = requests.post(url, files=files, ...)