I'm trying to spin up a single page flask application that allows users to download a word document. I've already figured out how to make/save the document using python-docx, but now I need to make the document available in the response. Any ideas?
Here's what I have so far:
from flask import Flask, render_template
from docx import Document
from cStringIO import StringIO
#app.route('/')
def index():
document = Document()
document.add_heading("Sample Press Release", 0)
f = StringIO()
document.save(f)
length = f.tell()
f.seek(0)
return render_template('index.html')
instead of render_template('index.html') you can just:
from flask import Flask, render_template, send_file
from docx import Document
from cStringIO import StringIO
#app.route('/')
def index():
document = Document()
document.add_heading("Sample Press Release", 0)
f = StringIO()
document.save(f)
length = f.tell()
f.seek(0)
return send_file(f, as_attachment=True, attachment_filename='report.doc')
You could use the send_from_directory as in this answer.
If you are sending text, you could also use the make_response helper as in this answer.
Use
return Response(generate(), mimetype='text/docx')
Generate() should be replaced with f in your case
For more information look at streaming in flask
http://flask.pocoo.org/docs/1.0/patterns/streaming/
For those how pass after me...
referring to these two links:
python 3.x ImportError: No module named 'cStringIO' [StackOverFlow Question]
TypeError: string argument expected, got 'bytes' [GitHub issue]
io.StringIO now replaces cStringIO.StringIO
also it will raise an error
as document.save(f) should receive a pass or binary file
code should be like this:
from flask import Flask, render_template, send_file
from docx import Document
from io import BytesIO
#app.route('/')
def index():
document = Document()
f = BytesIO()
# do staff with document
document.save(f)
f.seek(0)
return send_file(
f,
as_attachment=True,
attachment_filename='report.docx'
)
Related
I am going to create an API that converts an HTML page to a PDF file. I made it using pdfkit and FastAPI. However, it saves the file to my local disk. After I serve this API online, how could users download this PDF file to their computer?
from typing import Optional
from fastapi import FastAPI
import pdfkit
app = FastAPI()
#app.post("/htmltopdf/{url}")
def convert_url(url:str):
pdfkit.from_url(url, 'converted.pdf')
Returning FileResponse is solved my problem. Thanks to #Paul H and #clmno
Below codes are working example of returning pdf file to download with FastApi.
from typing import Optional
from fastapi import FastAPI
from starlette.responses import FileResponse
import pdfkit
app = FastAPI()
config = pdfkit.configuration(wkhtmltopdf=r"C:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe")
#app.get("/")
def read_root():
pdfkit.from_url("https://nakhal.expo.com.tr/nakhal/preview","file.pdf", configuration=config)
return FileResponse(
"file.pdf",
media_type="application/pdf",
filename="ticket.pdf")
**2)**This is another way with using tempfiles - to add pdf to a variable just write False instead of path -
from typing import Optional
from fastapi import FastAPI
from starlette.responses import FileResponse
import tempfile
import pdfkit
app = FastAPI()
config = pdfkit.configuration(wkhtmltopdf=r"C:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe")
#app.get("/")
def read_root():
pdf = pdfkit.from_url("https://nakhal.expo.com.tr/nakhal/preview",False, configuration=config)
with tempfile.NamedTemporaryFile(mode="w+b", suffix=".pdf", delete=False) as TPDF:
TPDF.write(pdf)
return FileResponse(
TPDF.name,
media_type="application/pdf",
filename="ticket.pdf")
Once you get the bytes of the PDF file, you can simply return a custom Response, specifying the content, headers and media_type. Thus, no need for saving the file to the disk or generating temporary files, as suggested by another answer. Similar to this answer, you can set the Content-Disposition header to let the browser know whether the PDF file should be viewed or downloaded.
Example
from fastapi import Response
#app.get('/')
def main():
pdf = pdfkit.from_url('http://google.com', configuration=config)
headers = {'Content-Disposition': 'attachment; filename="out.pdf"'}
return Response(pdf, headers=headers, media_type='application/pdf')
To have the PDF file viewed in the borwser instead of downloaded, use:
headers = {'Content-Disposition': 'inline; filename="out.pdf"'}
See this answer on how to install and use pdfkit.
currently I'm using "send_file" from flask, but it makes a read on the path to open the file. I already have loaded the file and want to return it, though I can't find a function for that. Any Ideas?
Example:
return send_file(filename_or_fp="../file.jpg", mimetype='image/jpeg')
I want to return it like that:
file = qrcode.make("sample text")
return send_file(filename_or_fp=file, mimetype='image/jpeg')
send_files returns following error: AttributeError: 'Image' object has no attribute 'read'
You need to save file to a BytesIO buffer, then pass that to the send_file function.
Also, you don't need to write filename_or_fp= as that is a positional argument.
The app should look like:
import qrcode
from flask import Flask, send_file
import io
app = Flask(__name__)
#app.route('/')
def index():
file = qrcode.make('sample text')
buf = io.BytesIO()
file.save(buf)
buf.seek(0)
return send_file(buf, mimetype='image/jpeg')
I am trying to download a pdf from the link :
https://ptenantectdtest.blob.core.windows.net/documentcontainer/fae488ce-514d-4367-be48-610b19193e10?sv=2015-12-11&sr=b&sig=It1gKsb%2BHmQwjqxAprbAROySOKAdd2qyFnW%2FoBi0uM0%3D&st=2019-07-18T18%3A20%3A05Z&se=2019-07-19T18%3A30%3A05Z&sp=r&rscd=attachment%3B%20filename%3D%20%228d4508bf-453e-45fd-8457-8fd158152ba7.pdf%22
When I use requests.content inside a python function, it works well and downloads the pdf but when I use the same inside app.route, it saves a corrupt pdf file.
Code for normal python function using requests:
def download_url(url):
r = requests.get(url)
with open('D:/file_.pdf', 'wb') as f:
f.write(r.content)
categories = convert('D:/file_.pdf')
return categories
Code for downloading from app.route:
import requests
import ectd
from ectd import convert
from flask import Flask
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
class ectdtext(Resource):
def get(self, result):
return {'data': ectd.convert(result)}
#app.route('/', defaults={'path': ''})
#app.route('/<path:path>')
def get_dir(path):
r = requests.get(path)
with open('D:/3file_.pdf', 'wb') as f:
f.write(r.content)
categories = convert('D:/3file_.pdf')
return categories
if __name__ == '__main__':
app.run()
I'm new at Flask. I try to pass a parameter from the main, but it doesn't work. Hope somebody can help me. Here is my code
from flask import Flask, render_template, request
import controllFlask
import pickle
app = Flask(__name__) # creates a flask object
import subprocess
#app.route('/', methods=['GET', 'POST'])
def ner():
'''controlls input of Webapp and calls proccessing methods'''
knownEntities = pickle.load( open( "entityDictThis.p", "rb" ))
print("loades")
content = ""
if request.method == 'POST':
testText = (request.form['input'])
app.ma = controllFlask.controll(testText, knownEntities)
subprocess.call("controllFlask.py", shell=True)
with open("test.txt", "r") as f:
content = f.read()
return render_template("ner.jinja2", content=content)
def flaskApp():
app.debug = True
app.run()
I want to open entityDictThis in flaskApp and give it to the ner-function. Because I hope in this way it loads only one time. At the moment it loads every time the page is reloaded and it takes very long. Is there a easy way?
This seems to be only a scoping problem, simply put the line that loads the pickle file in the scope above and it should solve the issue.
from flask import Flask, render_template, request
import controllFlask
import pickle
app = Flask(__name__) # creates a flask object
import subprocess
knownEntities = pickle.load( open( "entityDictThis.p", "rb" ))
#app.route('/', methods=['GET', 'POST'])
def ner():
'''controlls input of Webapp and calls proccessing methods'''
print("loades")
content = ""
if request.method == 'POST':
testText = (request.form['input'])
app.ma = controllFlask.controll(testText, knownEntities)
subprocess.call("controllFlask.py", shell=True)
with open("test.txt", "r") as f:
content = f.read()
return render_template("ner.jinja2", content=content)
def flaskApp():
app.debug = True
app.run()
I would also suggest, as #bouteillebleu mentioned, to close the loaded file, using the with keyword, which does this automagically for you.
with open( "entityDictThis.p", "rb" ) as f:
knownEntities = pickle.load(f)
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 )