Convert received json to dictionary, and save to local disk - python

My flask server receives a json file.
#app.route('/process', methods=['POST'])
def trigger_processing():
sent_files = request.files
json_file = sent_files['file']
print(json_file)
data = json.load(json_file)
print(data)
return make_response(json.dumps({'fileid': "ok"}), 200)
Printing the json_file gives me:
<FileStorage: 'test.json' ('application/octet-stream')>
127.0.0.1 - - [31/May/2021 23:29:55] "POST /process HTTP/1.1" 200 -
However the program fails when i try to convert json to dictionary.
json.decoder.JSONDecodeError: Expecting ',' delimiter: line 11 column 5 (char 213)
I want to be able to covert the json to dictionary.
Ideally, i would also like to save the received json file on the server's filesystem.

It fails because sent_files['file'] is a FileStorage object type, you need to read it and decode the bytes readed into a string. Then you can load it as a json.
from flask import Flask, request
from flask import make_response
from tempfile import gettempdir
import json
import os
app = Flask(__name__)
#app.route('/process', methods=['POST'])
def trigger_processing():
sent_files = request.files
#read the file
json_file_content = sent_files['file'].read().decode('utf-8')
#load the string readed into json object
json_content = json.loads(json_file_content)
print(json_content)
#generate the path where you want to save your file (in my case the temp folder)
save_path = os.path.join(gettempdir(), sent_files['file'].filename)
#and save the file
with open(save_path, 'w') as outfile:
json.dump(json_content, outfile, indent=2)
return make_response(json.dumps({'fileid': "ok"}), 200)

Related

I need help creating a simple python script that stores an attribute value from a custom json file

JSON file looks like this:
{"Clear":"Pass","Email":"noname#email.com","ID":1234}
There are hundreds of json files with different email values, which is why I need a script to run against all files.
I need to extract out the value associated with the Email attribute, which is nooname#email.com.
I tried using import json but I'm getting a decoder error:
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
Script looks like this:
import json
json_data = json.loads("file.json")
print (json_data["Email"]
Thanks!
According to the docs, json.loads() takes a str, bytes or bytearray as argument. So if you want to load a json file this way, you should pass the content of the file instead of its path.
import json
file = open("file.json", "r") # Opens file.json in read mode
file_data = file.read()
json_data = json.loads(file_data)
file.close() # Remember to close the file after using it
You can also use json.load() which takes a FILE as argument
import json
file = open("file.json", "r")
json_data = json.load(file)
file.close()
your script needs to open the file to get a file handle, than we can read the json.
this sample contains code that can read the json file. to simulate this, it uses a string that is identical with the data coming from the file.
import json
#this is to read from the real json file
#file_name = 'email.json'
#with open(file_name, 'r') as f_obj:
#json_data = json.load(f_obj)
# this is a string that equals the result from reading json file
json_data = '{"Clear":"Pass","Email":"noname#email.com","ID":1234}'
json_data = json.loads(json_data)
print (json_data["Email"])
result: noname#email.com
import json
with open("file.json", 'r') as f:
file_content = f.read()
#convert json to python dict
tmp = json.loads(file_content)
email = tmp["Email"]
As already pointed out in previous comments, json.loads() take contents of a file rather than a file.

Handle multiple request at same time on Flask web app

What I have: I've a Flask web app deployed to Heroku's server, which consists of only one web process app.py. Here it is:
#importation
from flask import Flask, render_template, current_app, send_file, request, json, send_file
import os
#working functions
#json write
def json_write(dictionary):
with open("./json/info.json", "w+") as f:
json.dump(dictionary, f, indent=4)
#make file name
def make_file_name(name):
filename = "tube4u_"
for t in str(name):
if t.isalnum():
filename += t
filename += ".mp4"
return filename
#application initialisation
app=Flask(__name__)
#home
#app.route("/")
def home():
return render_template("index.html")
#processor
#app.route("/process/", methods=["GET"])
def process():
#get url
url = request.args["url"]
#import & initialisation
from pytube import YouTube
import pickle
json_dict = {}
try:
yt = YouTube(url)
except:
return "<h1>Invalid URL</h1>"
all_videos = yt.streams.filter(type="video", progressive=True)
json_dict["title"] = yt.title
json_dict["thumbnail"] = yt.thumbnail_url
json_dict["name"] = make_file_name(yt.title)
with open("./pickle/ytobj.pkl", "wb") as f:
pickle.dump(all_videos, f)
#videos with itag
json_dict["videos"] = [ {"itag": item.itag, "res": item.resolution} for item in all_videos]
json_write(json_dict)
return render_template("menu.html")
#download
#app.route("/download/", methods=["GET"])
def download():
import pickle
itag = int(request.args["itag"])
with open("./json/info.json") as f:
json_dict = json.load(f)
with open("./pickle/ytobj.pkl", "rb") as f:
all_videos = pickle.load(f)
video = all_videos.get_by_itag(itag)
video.download(output_path="./video", filename=f"{json_dict['name']}")
return render_template("thank.html")
#return video
#app.route("/video/", methods=["GET"])
def video():
filename = request.args["filename"]
return send_file(f"./video/{filename}", as_attachment=True)
#return json
#app.route("/json")
def fetchjson():
with open("./json/info.json") as f:
content = json.load(f)
return content
#get name
#app.route("/name")
def fetchname():
with open("./json/info.json") as f:
content = json.load(f)
return content
#app.route("/list")
def listall():
return f"{os.listdir('./video')}"
#running the app
if __name__ == "__main__":
app.run(debug=True)
How it works: here I made the app like that, whenever someone enter a URL and click Go then it creates a json file with the name info.json. after it gets everything properly it performs some task with the given URL reading from the file.
My problem:
Now the problem is, if I make a request of the web it will create a json with my given URL, suppose at the same time someone else make a request and enter a URL then server will lost my information and rewrite the json file with another client's given input URL my task will be performed with another's input url. It's really weird.
How to fix it? Like if there any way to create the info.json file on separate path for each client and gets deleted after work done?
There is a lot of ways in my point of view
When the server get client request then check if there is already a file.if there is already a file then add timestamp or add something else in the filename so the file will not be overwritten.
Ask the user file name and also add timestamp in the name and save it.
You can also use databases to store data of different clients .may be you can create login system and give every user an id and store data for every user in database accordingly.
So on...
You can see there is a lot of ways to solve this.

Create response with file as content

I'd like to create an http response with an mp4 video as the content
Following the advice I found here this is what I have so far:
resp = Response()
resp.status_code = 200
with open("unit_tests/unit_test_data/testvid 2 - public_2.mp4", "rb") as vid:
resp._content = vid.read(1024 * 1024)
resp.headers = {"content-length": "13837851"}
This response is then to be used as follows :
with open(fname, "wb") as file:
for data in resp.iter_content(chunk_size=1024):
size = file.write(data)
However in the second part, when trying to read the content of my response I get the following error:
AttributeError: 'NoneType' object has no attribute 'read'
Any help is welcome, thanks!
Here are multiple snippets for the logic that can help you. (not tested, so might need some corrections)
myFileNameToSend = "unit_tests/unit_test_data/testvid 2 - public_2.mp4"
Step 1: Encode the file you want to send as base64 string
import base64
myFileToSend = open(myFileNameToSend, 'rb')
myFileContent = myFileToSend.read()
myFileStringToSend = base64.b64encode(myFileContent).decode('ascii')
Step 2: Actually sending the string over as response
from fastapi import Request,FastAPI, File, UploadFile, Form
import requests
api = FastAPI()
#api.get("/test") #this is where your webserver is listening for post requests
def test():
return {"filename": myFileNameToSend.split("/")[-1],"dataAsAscii":myFileStringToSend}
Step 3: Decoding base64 string back to bytes
import json
import base64
import requests
jsonReceived = initialRequest.json() #initialRequest is your request object to which you are expecting to get a response
myFileNameReceived = jsonReceived["filename"]
myFileStringReceived = jsonReceived["dataAsAscii"].encode('ascii')
myFileReceived = base64.b64decode(myFileStringReceived)
f = open(myFileNameReceived, 'wb')
f.write(myFileReceived)
f.close()

TypeError: 'Response' object is not iterable for BytesIO() stream

I am trying to return a response to a user can download the CSV from the browser. Even though I am successfully making a CSV for some response when I return the response I get the error TypeError: 'Response' object is not iterable
from flask import make_response
import csv
from io import BytesIO
new_csvfile = BytesIO()
wr = csv.writer(new_csvfile, quoting=csv.QUOTE_ALL)
for row in result:
wr.writerow(row)
#print new_csvfile.getvalue()
output = make_response(new_csvfile.getvalue())
output.headers["Content-Disposition"] = "attachment; filename=export.csv"
output.headers["Content-type"] = "text/csv"
return output
I am writing the CSV into a stream BytesIO() and its successfully creating a csv from what im seeing. But when I return the response to the user in the browser I get the following error TypeError: 'Response' object is not iterable
the result variable contains the data that I am writing to an CSV
If I have print new_csvfile.getvalue() the csv is fine and returns the below:
"1","2","3","4","5"
"1","2","3","4","5"
make_response doesn't support BytesIO type object. It takes the Response type and some other args like status code and headers. Modified code with jsonify for creating Response object.
from flask import make_response, jsonify
import csv
from io import BytesIO
new_csvfile = BytesIO()
wr = csv.writer(new_csvfile, quoting=csv.QUOTE_ALL)
for row in result:
wr.writerow(row)
resp_data = {
'data': new_csvfile.getvalue()
}
output = make_response(jsonify(resp_data))
output.headers["Content-Disposition"] = "attachment; filename=export.csv"
output.headers["Content-type"] = "text/csv"
return output
Some reference links for more info.
make response
kite docs

Flask to return image stored in database

My images are stored in a MongoDB, and I'd like to return them to the client, here is how the code is like:
#app.route("/images/<int:pid>.jpg")
def getImage(pid):
# get image binary from MongoDB, which is bson.Binary type
return image_binary
However, it seems that I can't return binary directly in Flask? My idea so far:
Return the base64 of the image binary. The problem is that IE<8 doesn't support this.
Create a temporary file then return it with send_file.
Are there better solutions?
Create a response object with the data and then set the content type header. Set the content disposition header to attachment if you want the browser to save the file instead of displaying it.
#app.route('/images/<int:pid>.jpg')
def get_image(pid):
image_binary = read_image(pid)
response = make_response(image_binary)
response.headers.set('Content-Type', 'image/jpeg')
response.headers.set(
'Content-Disposition', 'attachment', filename='%s.jpg' % pid)
return response
Relevant: werkzeug.Headers and flask.Response
You can pass a file-like object and the header arguments to send_file to let it set up the complete response. Use io.BytesIO for binary data:
return send_file(
io.BytesIO(image_binary),
mimetype='image/jpeg',
as_attachment=True,
download_name='%s.jpg' % pid)
Prior to Flask 2.0, download_name was called attachment_filename.
Just wanted to confirm that dav1d's second suggestion is correct - I tested this (where obj.logo is a mongoengine ImageField), works fine for me:
import io
from flask import current_app as app
from flask import send_file
from myproject import Obj
#app.route('/logo.png')
def logo():
"""Serves the logo image."""
obj = Obj.objects.get(title='Logo')
return send_file(
io.BytesIO(obj.logo.read()),
download_name='logo.png',
mimetype='image/png'
)
Easier than manually creating a Response object and settings its headers.
Prior to Flask 2.0, download_name was called attachment_filename.
Suppose i have the stored image path with me. The below code helps to send image through.
from flask import send_file
#app.route('/get_image')
def get_image():
filename = 'uploads\\123.jpg'
return send_file(filename, mimetype='image/jpg')
uploads is my folder name where my image with 123.jpg is present.
[PS: The uploads folder should be in the current directory as of the your script file]
Hope it helps.
The following worked for me (for Python 3.7.3):
import io
import base64
# import flask
from PIL import Image
def get_encoded_img(image_path):
img = Image.open(image_path, mode='r')
img_byte_arr = io.BytesIO()
img.save(img_byte_arr, format='PNG')
my_encoded_img = base64.encodebytes(img_byte_arr.getvalue()).decode('ascii')
return my_encoded_img
...
# your api code
...
img_path = 'assets/test.png'
img = get_encoded_img(img_path)
# prepare the response: data
response_data = {"key1": value1, "key2": value2, "image": img}
# return flask.jsonify(response_data )

Categories