web-dev-qa-db-fra.com

Requêtes Python: Post JSON et fichier en une seule demande

Je dois effectuer un appel API pour télécharger un fichier avec une chaîne JSON contenant des détails sur le fichier.

J'essaie d'utiliser les requêtes python lib pour faire ceci:

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)

Cela lève l'erreur suivante:

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

Si je supprime les "fichiers" de la demande, cela fonctionne.
Si je supprime les 'données' de la demande, cela fonctionne.
Si je ne code pas les données en JSON, cela fonctionne.

Pour cette raison, je pense que l'erreur est liée à l'envoi de données et de fichiers JSON dans la même demande.

Des idées sur la façon de faire fonctionner cela?

23
oznu

Ne pas encoder en utilisant 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)

Notez que cela peut ne pas être nécessairement ce que vous voulez, car cela deviendra une autre section de données de formulaire.

17
proteneer

Voir ce fil Comment envoyer un fichier JSON dans le cadre d'une requête POST en plusieurs parties

(Ne définissez pas l'en-tête Content-type vous-même, laissez les pyrequests le générer) 

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)
9
ralf htp

Je ne pense pas que vous puissiez envoyer à la fois des données et des fichiers dans un fichier codé en plusieurs parties.

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

r = requests.post(url, files=files, headers=headers)
2
AChampion

Pour l'envoi de l'API Facebook Messenger, j'ai changé toutes les valeurs du dictionnaire de données utiles en chaînes. Ensuite, je peux transmettre la charge utile en tant que paramètre data.

import requests

ACCESS_TOKEN = ''

url = 'https://graph.facebook.com/v2.6/me/messages'
payload = {
        'access_token' : ACCESS_TOKEN,
        'messaging_type' : "UPDATE",
        'recipient' : '{"id":"1111111111111"}',
        'message' : '{"attachment":{"type":"image", "payload":{"is_reusable":true}}}',
}
files = {'filedata': (file, open(file, 'rb'), 'image/png')}
r = requests.post(url, files=files, data=payload)
0
wannik

De plus:

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

Cela ne fonctionnera que si votre fichier se trouve dans le même répertoire que votre script.

Si vous souhaitez ajouter un fichier à partir d'un répertoire différent, procédez comme suit:

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

dir_path est un répertoire contenant votre fichier 'nom_fichier.pdf'

Mais que se passe-t-il si vous souhaitez envoyer plusieurs fichiers PDF?

Vous pouvez simplement créer une fonction personnalisée pour renvoyer une liste des fichiers dont vous avez besoin (dans votre cas, il ne peut s'agir que de ceux avec l'extension .pdf). Cela inclut également les fichiers de sous-répertoires (recherche de fichiers de manière récursive):

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')])

Ensuite, vous pouvez l'appeler:

my_data = prepare_pdfs()

Et avec une simple boucle:

for file in my_data:

    pdf = open(file, 'rb')

    files = {
        'document': pdf
    }

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