Flask send image with curl as json payload - python

I have a simple flask API with file upload to send an image:
from flask import request
from flask import Flask
app = Flask(__name__)
#app.route('/', methods=['POST'])
def upload_file():
f = request.files['image']
print(f.content_type)
print(f.filename)
# do something with the file
# f.read()
return("Done")
if __name__ == "__main__":
app.run(debug=True, host='0.0.0.0', port=5000)
When I send an image using curl's -F option, I can access the file:
curl -XPOST localhost:5000 -F "image=#img.jpg"
But how can I send an image as a payload in a json file with the -d option? If I encode the image using base64, put the string into a json file and send it with the -d option, it cannot find my image: KeyError: 'image'
I tried with different content types:
curl -XPOST localhost:5000 -d #img.json --header "Content-Type: application/json"
curl -XPOST localhost:5000 -d #img.json --header "Content-Type: image/jpeg"
My image.json looks something like this:
{"image":"/9j/4AAQSkZJRgABAQEAS..."}
(with the complete string instead of ...)

If you are sending data as JSON payload, you will have to use request.json to access them. Flask docs for files attribute state, that this attribute is non-empty when enctype="multipart/form-data". curl will set multipart/form-data header implicitly when you use -F option.
But for json data you have to set --header "Content-Type: application/json" as you have in you example. However, you have to check request.mimetype on the server side. Based on that check, you will either use files or json attribute. Here is the example how to handle those 2 cases:
from flask import request
from flask import Flask
app = Flask(__name__)
#app.route('/', methods=['POST'])
def upload_file():
if request.mimetype == 'multipart/form-data':
f = request.files['image']
print(f.content_type)
print(f.filename)
# do something with the file
# f.read()
else if request.mimetype == 'application/json':
f = request.json['image']
print(f)
# decode f from Base64
else:
# handle other mimetypes or return that selected mimetype is not supported
print('Unsupported content-type')
return("Done")
if __name__ == "__main__":
app.run(debug=True, host='0.0.0.0', port=5000)
The curl command you used should be handled correctly:
curl -XPOST localhost:5000 -d #img.json --header "Content-Type: application/json"

Related

The file is changed in the process of saving to the server

I work with a local server via Flask. The server must accept a POST request with a .log file and save it to itself. But the file is saved, changing.
curl request example
curl -d "#12333.log" -X POST http://127.0.0.1:5000/test/45322.log
python file
import os
from datetime import datetime
from flask import Flask, request
app = Flask(__name__)
SERVER_CONFIG = {'debug': True}
DATA_DIR = './data'
#app.route('/<uid>/<name>', methods=['POST'])
def main(uid, name):
service = Service(uid)
data = request.get_data()
response = service.get_data(data, name)
return response
class Service:
def __init__(self, uid):
self._make_dir(uid)
self.uid = uid
def get_data(self, request_body, name):
timestamp = datetime.now()
with open(f'{DATA_DIR}/{self.uid}/{timestamp}_{name}', 'wb') as fd:
fd.write(request_body)
return {'success': True, 'status': 'body saved'}
def _make_dir(self, uid):
if not os.path.exists(f'{DATA_DIR}/{uid}'):
os.mkdir(f'{DATA_DIR}/{uid}')
if __name__ == '__main__':
app.run(**SERVER_CONFIG)
The file being sent 12333.log
23.09.2021 06:24:41;23.09.2021 06:24:41;7;2100.000;2400.000;600.000;89912.000;85947.000;91562.000;229.000;229.000;226.000;0.999;0.999;0.999;2100.000;2400.000;600.000;90215.000;87121.000;92000.000;229.000;229.000;226.000;0.999;0.999;0.999;257400.000;3900.000;257400.000;0;0.000;0;0.000;35;0;
23.09.2021 07:25:00;23.09.2021 07:25:00;7;900.000;1500.000;600.000;80549.000;79915.000;93058.000;230.000;230.000;225.000;0.999;0.999;0.999;900.000;1500.000;600.000;81500.000;80519.000;93900.000;230.000;230.000;225.000;0.999;0.999;0.999;262200.000;4200.000;262200.000;0;0.000;0;0.000;43;0;
23.09.2021 08:25:03;23.09.2021 08:25:03;8;1800.000;900.000;600.000;87025.000;84954.000;89294.000;230.000;229.000;227.000;0.999;0.999;0.999;1800.000;900.000;600.000;87451.000;85121.000;90905.000;230.000;229.000;227.000;0.999;0.999;0.999;274200.000;3900.000;274500.000;0;0.000;0;0.000;33;0;
23.09.2021 09:25:16;23.09.2021 09:25:16;4;600.000;900.000;0.000;84913.047;80869.562;86413.047;229.000;231.000;226.000;0.999;0.999;0.999;600.000;900.000;0.000;87900.000;80700.000;85200.000;229.000;231.000;226.000;0.999;0.999;0.999;235500.000;3000.000;235800.000;0;0.000;0;0.000;18;0;
The file that is being saved
23.09.2021 06:24:41;23.09.2021 06:24:41;7;2100.000;2400.000;600.000;89912.000;85947.000;91562.000;229.000;229.000;226.000;0.999;0.999;0.999;2100.000;2400.000;600.000;90215.000;87121.000;92000.000;229.000;229.000;226.000;0.999;0.999;0.999;257400.000;3900.000;257400.000;0;0.000;0;0.000;35;0;23.09.2021 07:25:00;23.09.2021 07:25:00;7;900.000;1500.000;600.000;80549.000;79915.000;93058.000;230.000;230.000;225.000;0.999;0.999;0.999;900.000;1500.000;600.000;81500.000;80519.000;93900.000;230.000;230.000;225.000;0.999;0.999;0.999;262200.000;4200.000;262200.000;0;0.000;0;0.000;43;0;23.09.2021 08:25:03;23.09.2021 08:25:03;8;1800.000;900.000;600.000;87025.000;84954.000;89294.000;230.000;229.000;227.000;0.999;0.999;0.999;1800.000;900.000;600.000;87451.000;85121.000;90905.000;230.000;229.000;227.000;0.999;0.999;0.999;274200.000;3900.000;274500.000;0;0.000;0;0.000;33;0;23.09.2021 09:25:16;23.09.2021 09:25:16;4;600.000;900.000;0.000;84913.047;80869.562;86413.047;229.000;231.000;226.000;0.999;0.999;0.999;600.000;900.000;0.000;87900.000;80700.000;85200.000;229.000;231.000;226.000;0.999;0.999;0.999;235500.000;3000.000;235800.000;0;0.000;0;0.000;18;0;
Update your curl command. Replace -d to --data-binary:
curl --data-binary "#12333.log" -X POST http://127.0.0.1:5000/test/45322.log
The reason is that, when using -d or --data option to upload a file, curl command will remove carriage returns and newlines. --data-binary won't do that.

uploading flie to FastAPI endpoint using curl - 307 Temporary Redirect

I have a fastAPI endpoint that recieves a file and saves it to disk as follows:
from fastapi import FastAPI, File, UploadFile
import shutil
app = FastAPI()
#app.post('/upload')
async def upload_file(file: UploadFile=File(...)):
with open(file.filename, "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
return {
"filename": file.filename,
}
This works as expected when I upload a file through the docs interface at http://localhost:8000/docs
I am able to select a file and it successfully uploads.
However, attempting the same with curl fail:
curl -X POST localhost:8000/upload -F file=#photo.png
the curl command returns nothing and on the server side a 307 Temporary Redirect is logged.
I am not sure what I am missing here
In some scenario/setup where FastAPI redirect the request, use the curl with full dns/ip address.
something like this :
curl -X 'POST' '127.0.0.1:8000/upload' -F 'file=#photo.png
or can add headers(-H) too based on the application that is built.
curl -X 'POST' \
'http://127.0.0.1:8000/upload' \
-H 'accept: application/json' \
-H 'Content-Type: multipart/form-data' \
-F 'file=#photo.png;type=application/json'

Have issue during curl URL for REST API used Flask url: (35) schannel: next InitializeSecurityContext failed

I have design web app using Flask for REST API server
for get id and key from frontend, backend will get info and do some action
(only using POST method)
curl command
curl -X POST -H "Content-Type:application/json" --data "{/"account_id/":/"100002/", /"access_key/":/"AKIAWDL6TY5M2INS6J7E/"}" https://192.168.172.130:443/account
However, when I am using curl command as below:
X POST -H "Content-Type:application/json" --data "{/"account_id/":/"100002/", /"access_key/":/"AKIAWDL6TY5M2INS6J7E/"}" https://192.168.172.130:443/account
curl: (35) schannel: next InitializeSecurityContext failed: SEC_E_INVALID_TOKEN (0x80090308) - The token supplied to the function is invalid
code design in run.py
def scan_account(_account_id:str, _access_key:str):
# building connection to db
mySQLDB = mysqlDBConnector()
mySQLDB.dbConnection()
#init record log request
_now_time = datetime.datetime.now()
_request_info_log:str = 'Request of account id:'+str(_account_id)+' With Access Key: '+str(_access_key)+' at: '+str(_now_time)+' direction data: incoming with action type: post request'
mySQLDB.db_log_request_insert(_request_info_log)
# get secret key
_AccountID: int = _account_id
_AccessKey: str = _access_key
_SecretKey: str = mySQLDB.db_get_key(_AccountID,_AccessKey)
# init boto3 session
_aws_session = AWS_Session(_AccessKey, _SecretKey)
_aws_session.get_credentials()
#init running
_worker = Worker()
attrs = (getattr(_worker, name) for name in dir(_worker))
methods = filter(inspect.ismethod, attrs)
for method in methods:
_thread_method = threading.Thread(target=method, args=(_aws_session,))
_thread_method.start()
_thread_method.join()
#app.route("/account/",methods=["POST"])
def account_info():
_account_id = request.json['account_id']
_access_key = request.json['access_key']
#data = {'acount_id': _account_id, 'access_key': _access_key}
scan_account(_account_id,_access_key)
#return jsonify(data)
if __name__ == '__main__':
app.run(debug=True,host='0.0.0.0', port='443')
Ok lets get a couple of things out the way, I DO NOT suggest you use this for anything other than a local dev. Please use proper SSL.
Make sure you have pyOpenSSL properly installed.
from flask import Flask, jsonify, request, make_response
app = Flask(__name__)
#app.route("/account/",methods=["POST"])
def account_info():
_account_id = request.json['account_id']
_access_key = request.json['access_key']
data = {'acount_id': _account_id, 'access_key': _access_key}
return make_response(jsonify(data), 200)
if __name__ == '__main__':
app.run(debug=True,host='0.0.0.0', port='433', ssl_context='adhoc')
I also modified your curl to make it simpler, that and it was causing issues:
curl -X POST -H "Content-Type:application/json" --data '{"account_id":"100002", "access_key":"AKIAWDL6TY5M2INS6J7E"}' https://localhost:9443/account/ --insecure
I get the following output:
{
"access_key": "AKIAWDL6TY5M2INS6J7E",
"acount_id": "100002"
}
This is how i tested it all:
docker run --rm -it -p 9443:443 python:3.7 bash -c '
pip install flask pyOpenSSL;
curl -s https://gist.githubusercontent.com/kingbuzzman/a955b49a318eef9e76b4bf9026cd2595/raw/sample.py > sample.py;
python sample.py'
Here is the gist source: https://gist.github.com/kingbuzzman/a955b49a318eef9e76b4bf9026cd2595

Consume JSON body, not form data

This is my Python. The input to it is
curl http://35.173.47.24:8090/put -d {"id":102} -X PUT
My code:
from flask import Flask, request
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
todos = {}
class TodoSimple(Resource):
def put(self):
i=request.form['id']
print(i)
api.add_resource(TodoSimple, '/put')
if __name__ == '__main__':
app.run(host='0.0.0.0',port=8090,debug=True)
I need to pass json parameter. What do I have to change?
change the request to
curl -X PUT http://25.173.47.24:8080/put -d '{"id":102}' -H "Content-Type:application/json"
issue resolved issue was with code . request.get_json should be used

Using PUT to receive an xml file in webpy

I am trying to receive an xml file through PUT in web.py, it is not working.Can any one explain what is the issue in the below code
import web
urls = (
'/', 'index'
)
class index:
def PUT(self):
postdata = web.data().read()
fout = open('/home/test/Desktop/e.xml','w')
fout.write(postdata)
fout.close()
return "Hello, world!"
if __name__ == "__main__":
app = web.application(urls, globals())
app.run()
I am getting this is terminal
"HTTP/1.1 PUT /doc.xml" - 404 Not Found
I use curl to upload the xml
curl -o log.out -H "Content-type: text/xml; charset=utf-8" -T doc.xml "http://0.0.0.0:8760"
import web
urls = ( '/upload', 'index')
class index:
def PUT(self):
datta = web.data()
with open("another.xml", "w") as f:
f.write(datta)
return "hello"
if __name__ == "__main__":
app = web.application(urls, globals())
app.run()
with this curl
curl -T somexml.xml http://0.0.0.0:8080/upload
worked for me. I needed to change url because curl behaved strange. Or maybe it seemed to me. but somehow this code won't work with "/" as a url.
You are using the wrong curl option.
If you want your file content in the request body, you should use -d instead of -T
curl -o log.out -H "Content-type: text/xml; charset=utf-8" -d doc.xml "http://0.0.0.0:8760"
EDIT:
Anyway, this will transform your curl into a POST request. To keep it as PUT, use -X PUT
curl -X PUT -o log.out -H "Content-type: text/xml; charset=utf-8" -d doc.xml "http://0.0.0.0:8760"

Categories