"Object not callable" when echoing posted image - python

I'm trying to listen for a file being sent over post with the key 'image', then return it back to the caller.
from flask import Flask, request, make_response
app = Flask(__name__)
#app.route('/', methods = ['POST', 'GET'])
def img():
if request.method == 'GET':
return "yo, you forgot to post"
else:
img = request.files['image']
resp = make_response(img)
resp.status_code = 200
return resp
if __name__ == "__main__":
app.debug = True
app.run(host='0.0.0.0')
This fails with the message:
TypeError: 'FileStorage' object is not callable
I'm not massively familiar with Python, but I thought by doing the above that I was passing the byte[] straight through memory?
How do you achieve this without it failing? I want to take it the image blob from the request, manipulate it (not currently), and return it straight back out. In memory, without saving it to the file system.

You can serve out the image again with the Flask.send_image() function; it accepts a file-like object as well as filenames. You'll have to copy the file data over to an in-memory file object (the request.files file objects are closed too early to be suitable):
from io import BytesIO
return send_file(BytesIO(request.files['image'].read())
You may want to add additional headers, such as the mime-type, to the response as the send_file() function has less information to go on here than with 'regural' file objects. The sending browser probably included a content type for example:
import mimetypes
img = request.files['image']
mimetype = img.mimetype or mimetypes.guess_type(img.filename)[0]
return send_file(BytesIO(img.read()), mimetype=mimetype)
This does require you to hold the whole image in memory.

Related

Setting a parameter in Post body to header when accessing Get endpoint in Flask?

I am working on a simple service with my show_greeting endpoint handling Get request while set_greeting is my Post.
The purpose of this app is that when "header_message: {header parameter}" is sent to set_greeting, {header parameter} will be returned in the header for responses to show_greeting and to reset {header parameter}, "clear" would reset header_message and header.
I have tried using global variables but encountered an error with shadowing from outside the scope and am not sure which approach to take for this. For now, I would like to learn how to return {header parameter} from my /show_greeting endpoint.
Edit: The /show_greeting endpoint returns holiday_message from the request. The header that I would like to send in addition to holiday_message is "header_message".
My code is as follows:
from flask import Flask, request, make_response, Response
app = Flask(__name__)
#app.route('/show_greeting', methods=['GET'])
def show_greeting():
received = request.args
(I do not know how to set header here from header_message in set_greeting)
return received['holiday_message']
#app.route('/set_greeting', methods=['POST'])
def set_greeting():
posted = request.args
if 'header_message' in posted:
(I attempted save_message = posted['header_message'] here but this approach failed)
return "Header Message Set"
else:
return "Please Send A Header Message"
if __name__ == '__main__':
app.run()
My recommendation is to use the session object. It stores the data in a cookie, which is sent with every request.
If a cookie is not desired, there are other options for saving sessions. For this, however, an extension will be necessary.
Saving with global variables should also work, but is not recommended.
A file or a database can also be used if the data is to be saved across multiple requests from many users.
The data of the post body can be accessed via request.form, while the url parameters of a get request can be queried via request.args.
from flask import Flask
from flask import request, session
app = Flask(__name__)
app.secret_key = b'your secret here'
#app.route('/show_greeting', methods=['GET'])
def show_greeting():
received = request.args
# get the saved message or an empty string if no message is saved
header_message = session.get('header_message', '')
return f"{received['holiday_message']} - {header_message}"
#app.route('/set_greeting', methods=['POST'])
def set_greeting():
posted = request.form
if 'header_message' in posted:
# store the message
session['header_message'] = posted['header_message']
return "Header Message Set"
else:
# clear the message
session.pop('header_message', None)
return "Please Send A Header Message"
Much success in your further steps.
If I understood your problem, you can work with "g" the flask global object.
Check this code, I expect it will fix your issue.
from flask import g # Added
from flask import Flask, request, make_response, Response
app = Flask(__name__)
#app.route('/show_greeting', methods=['GET'])
def show_greeting():
received = request.args
return g.saved_message # Modified
#app.route('/set_greeting', methods=['POST'])
def set_greeting():
posted = request.args
if 'message' in posted:
g.saved_message = posted['request'] # Added
return "Message Set"
else:
return "Please Send A Greeting Message"
if __name__ == '__main__':
app.run()

Python Flask : Returned file is not readable

While implementing a rest API in python flask, I have used several options to return a file (any type) , read it and save it to local repository of request but encountered with multiple errors as below:
Case 1:
def download_file():
return send_file('any_file.pdf')
r = requests.get(url = 'http://localhost:5000/download').read()
has responded with a error Response object has no attribute read/text/content
Case 2:
def download_file():
file = open('any_file.pdf','r').read()
return file
r = requests.get(url = 'http://localhost:5000/download')
has responded with a error Return doesn't accept this
So How can I do this as flask is not allowing to return a file without response object and response object is not readable and doesn't support to save that file directly.
The Flask server code in Case 1 is correct. A more complete example:
#app.route('/download')
def download_file():
# Some logic here
send_file('any_file.pdf')
However the Response object returned by requests.get doesn't have a read method. The correct way is to use:
Response.content: Content of the response, in bytes.
So, the client code should be:
r = requests.get('http://localhost:5000/download')
bytes = r.content
# Now do something with bytes, for example save it:
with open('downloaded_file.ext', 'wb') as f:
f.write(bytes)

POST method to upload file with json object in python flask app

I am stuck in a problem where I am trying to build single API which will upload file along with json object. I need this API to create webhook.
Using multi part, I am able to upload file and in option filed I am able to send json object.
In flask app when I am trying to retrieve json object its converting as blob type. I tried to convert it base64 then again converting into string but whole process is not working.
Let me know if anyone has good solution I can combine file and json object together and fetch it by flask python app.
zz is the variable in my code where I am trying to store my json object. name is the field where I am passing my json object with file.
Thanks in advance.
My current code
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
#app.route('/upload/',methods = ['POST'])
def upload():
customer_name='abc'
if request.method == 'POST':
zz=base64.b64encode(request.files['name'].read())
try:
file = request.files['file']
if file:
file.filename=customer_name+'_'+str(datetime.now())+'.'+file.filename.split('.')[-1]
filename = secure_filename(file.filename)
path=os.path.join(app.config['UPLOAD_FOLDER'], filename)
file.save(path)
return jsonify({
'status':success,
'junk_data':[],
'message':error
})
except Exception as err:
logger.error(str(datetime.now())+' '+str(err))
return jsonify({
'status':False,
'junk_data':[],
'message':str(err)
})
if __name__ == '__main__':
app.run(host='localhost',debug=True, use_reloader=True,port=5000)
I have got the answer after lot R&D.
request format
//user any client-side
content-type:multipart/form-data
file: file need to upload
data: {"name":"abc","new_val":1}
python code to fetch from request object
data=json.loads(request.form.get('data'))
file = request.files['file']
Just send the file and json at once and let request handle the rest.
You can try the code below:
Server side:
import json
import os
from flask import Flask, request
from werkzeug import secure_filename
app = Flask(__name__)
#app.route('/test_api',methods=['GET','POST'])
def test_api():
uploaded_file = request.files['document']
data = json.load(request.files['data'])
filename = secure_filename(uploaded_file.filename)
uploaded_file.save(os.path.join('path/where/to/save', filename))
print(data)
return 'success'
if __name__ == '__main__':
app.run(host='localhost', port=8080)
Client Side:
import json
import requests
data = {'test1':1, 'test2':2}
filename = 'test.txt'
with open(filename, 'w') as f:
f.write('this is a test file\n')
url = "http://localhost:8080/test_api"
files = [
('document', (filename, open(filename, 'rb'), 'application/octet')),
('data', ('data', json.dumps(data), 'application/json')),
]
r = requests.post(url, files=files)
print(r)

Using Flask as pass through proxy for file upload?

It's for app engine's blobstore since its upload interface generates a temporary endpoint every time. I'd like to take the comlexity out of frontend, Flask would take the post request and forward it to the end point specified by blobstore. Performance and traffic cost is not a concern at all, can someone recommend a most straightforward way to implement?
Looking at the docs for the BlobStore flow it looks like all you need to do is accept the file yourself and then send it on to the endpoint specified by create_upload_url:
#app.route("/upload-complete", methods=["POST"])
def handle_upload_response():
"""This will be called after every upload, but we can ignore it"""
return "Success"
#app.route("/upload", methods=["POST"])
def upload():
fp = request.files["name_of_file"]
url = create_upload_url(url_for('handle_upload_response'))
response = requests.post(url, {'file':
(fp.filename, fp.stream,
fp.content_type, fp.headers)})
if response == "Success":
return "File uploaded successfully"
else:
return "Something didn't work out"

Proper way to modify a response in Flask with eg process_response

Given a simple Flask application, I'm just curious about whether there is a proper way to modify a Response in the hooks such as process_response?
e.g. Given:
from flask import Flask, Response
class MyFlask(Flask):
def process_response(self, response):
# edit response data, eg. add "... MORE!", but
# keep eg mimetype, status_code
response.data += "... This is added" # but should I modify `data`?
return response
# or should I:
# return Response(response.data + "... this is also added",
# mimetype=response.mimetype, etc)
app = MyFlask(__name__)
#app.route('/')
def root():
return "abddef"
if __name__ == '__main__':
app.run()
Is it proper to just create a new response each time, or is it canonical to just edit in-place the response parameter and return that modified response?
This may be purely stylistic, but I'm curious – and I haven't noticed anything in my reading that would indicate the preferred way to do this (even though it's probably quite common).
Thanks for reading.
From the Flask.process_response docs:
Can be overridden in order to modify the response object before it's sent to the WSGI server.
The response object is created on flask dispacher mechanism (Flask.full_dispatch_request). So if you want to create response objects under your own way, override Flask.make_reponse. Use Flask.process_response only when the desired modifications can be made using the created response object parameter.
Actually, you can use Flask.process_response to intercept and modify the response this way:
from flask import Flask
import json
import ast
appVersion = 'v1.0.0'
class LocalFlask(Flask):
def process_response(self, response):
#Every response will be processed here first
response.headers['App-Version'] = appVersion
success = True if response.status_code in [ 200, 201, 204 ] else False
message = 'Ok' if success else 'Error'
dict_str = response.data.decode("UTF-8")
dataDict = ast.literal_eval(dict_str)
standard_response_data = {
'success': success,
'message': message,
'result': dataDict
}
response.data = json.dumps(standard_response_data)
super(LocalFlask, self).process_response(response)
return response

Categories