Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

using python to send json and files to flask

I have this issue where I am trying to send/receive to a flask API some file and JSON in a single function.

On my client ( sender ) I have :

#my json to be sent 
datas = {'var1' : 'var1','var2'  : 'var2',}
#my file to be sent 
local_file_to_send = 'user_picture.jpg'
url = "http://10.100.2.6:80/customerupdate"
headers = {'Content-type': 'multipart/form-data'}
files = {'document': open(local_file_to_send, 'rb')}
r = requests.post(url, files=files, data=datas, headers=headers)

On my Flask server I have :

class OPERATIONS(Resource):
        @app.route('/',methods=['GET'])
        def hello_world():
            return 'Hello World!'

        @app.route('/customerupdate',methods=['GET','POST'])
        def customerupdate():
            event_data_2 = json.loads(request.get_data().decode('utf-8'))
            print event_data_2

I have this error message telling me that the data is actually not a json format nor a utf8 format. If I print the content of the "get_data" without trying to decode it shows some binary characters..

What would be the syntax on my client to read the json and write the file locally ?

like image 245
MouIdri Avatar asked Dec 06 '17 16:12

MouIdri


3 Answers

I would recommend sending both the JSON and the file as parts of the multipart form. In that case you would read them from request.files on the server. (One caveat: I tested all my examples with Python 3, requests 2.18.4, and Flask 0.12.2 -- you might need to change the code around to match your environment).

From https://stackoverflow.com/a/35940980/2415176 (and the Flask docs at http://docs.python-requests.org/en/latest/user/advanced/#post-multiple-multipart-encoded-files), you don't need to specify headers or anything. You can just let requests handle it for you:

import json
import requests

# Ton to be sent
datas = {'var1' : 'var1','var2'  : 'var2',}

#my file to be sent
local_file_to_send = 'tmpfile.txt'
with open(local_file_to_send, 'w') as f:
    f.write('I am a file\n')

url = "http://127.0.0.1:3000/customerupdate"

files = [
    ('document', (local_file_to_send, open(local_file_to_send, 'rb'), 'application/octet')),
    ('datas', ('datas', json.dumps(datas), 'application/json')),
]

r = requests.post(url, files=files)
print(str(r.content, 'utf-8'))

Then on the server you can read from request.files (see http://flask.pocoo.org/docs/0.12/api/#flask.Request.files but note that request.files used to work a little differently, see https://stackoverflow.com/a/11817318/2415176):

import json                                                     

from flask import Flask, request                                

app = Flask(__name__)                                           

@app.route('/',methods=['GET'])                                 
def hello_world():                                              
    return 'Hello World!'                                       

@app.route('/customerupdate',methods=['GET','POST'])            
def customerupdate():                                           
    posted_file = str(request.files['document'].read(), 'utf-8')
    posted_data = json.load(request.files['datas'])             
    print(posted_file)                                          
    print(posted_data)                                          
    return '{}\n{}\n'.format(posted_file, posted_data)          
like image 65
Craig Kelly Avatar answered Oct 31 '22 12:10

Craig Kelly


Thanks to Craig answer, I found the solution. I will post both code ( client and server ) to help in case of future use. The CLient server is uploading file and Json in the "form" feature of flask. Then some ast and some dict to make the Payload clearer ( I know this is ugly way, but this is the best scholar approach )

On Client side :

datas = {'CurrentMail': "AA", 'STRUserUUID1': "BB", 'FirstName': "ZZ",     'LastName': "ZZ",  'EE': "RR", 'JobRole': "TT"  }

#sending user infos to app server using python "requests"
url = "http://10.100.2.6:80/customerupdate"
def send_request():
    payload = datas
    local_file_to_send = 'user_picture.jpg' 
    files = {
     'json': (None, json.dumps(payload), 'application/json'),
     'file': (os.path.basename(local_file_to_send), open(local_file_to_send, 'rb'), 'application/octet-stream')
    }
    r = requests.post(url, files=files)

send_request()

On Flask Server side :

import sys, os, logging, time, datetime, json, uuid, requests, ast
from flask import Flask, request , render_template
from werkzeug import secure_filename
from werkzeug.datastructures import ImmutableMultiDict
from flask_restful import Resource, Api

app = Flask(__name__)
api = Api(app)
app.debug = True

class OPERATIONS(Resource):
        @app.route('/',methods=['GET'])
        def hello_world():
            return 'Hello World!'

        @app.route('/customerupdate',methods=['GET','POST'])
        def customerupdate():
            print "************DEBUG 1 ***********"
            RequestValues = request.values
            print RequestValues
            print "************DEBUG 2 ***********"
            RequestForm = request.form
            print RequestForm
            print "************DEBUG 2-1 ***********"
            so = RequestForm
            json_of_metadatas = so.to_dict(flat=False)
            print json_of_metadatas
            print "************DEBUG 2-2 ***********"
            MetdatasFromJSON = json_of_metadatas['json']
            print MetdatasFromJSON          
            print "************DEBUG 2-3 ***********"
            MetdatasFromJSON0 = MetdatasFromJSON[0]
            print MetdatasFromJSON0
            print "************DEBUG 3-5 ***********"
            strMetdatasFromJSON0 = str(MetdatasFromJSON0)
            MetdatasDICT = ast.literal_eval(strMetdatasFromJSON0)
            print MetdatasDICT
            print "************DEBUG 3-5 ***********"
            for key in MetdatasDICT :
                print "key: %s , value: %s" % (key, MetdatasDICT[key])
            print "************DEBUG 4 ***********"
            f = request.files['file']
            f.save(secure_filename(f.filename))
            print "FILE SAVED LOCALY"
            return 'JSON of customer posted'
like image 37
MouIdri Avatar answered Oct 31 '22 12:10

MouIdri


If this is not in production, there is an easier way than binding the json in files, send in the json data as param value instead of binding it in json.

datas = {data: {'var1' : 'var1','var2'  : 'var2}}
url = "http://10.100.2.6:80/customerupdate"
files = {'document': open(local_file_to_send, 'rb')}
headers = {'Content-type': 'application/json'}
r = requests.post(url, files=files, params=datas, headers=headers)

and in flask server accept data and file as:

image = request.files.get('image')
data = request.args.get('data')
like image 20
neeraj mdas Avatar answered Oct 31 '22 12:10

neeraj mdas