I have created a plot in a view and saved the plot as a png inside the templates folder. But when I try to display this saved image using an <img> tag in a template html file, the image is not displayed.
Here is an image of my folder structure:
Folder Structure
This is how I save my plot inside a view:
def result(request):
if request.POST and request.FILES:
mycsv=pd.read_csv(request.FILES['csv_file'])
c=mycsv.X
#We divide by the class height(basically) so that Xi+1-Xi=1
x = [d / 5 for d in c]
n=len(x)
b=mycsv.Y
divi=np.sum(b)
f = [e / divi for e in b]
#Thus sum(f)=1, makes calculation of mean simpler
#PLOTTING BEGINS
fig = plt.figure()
ax = plt.subplot(111)
ax.plot(x, f)
plt.title('Pearson Type 1 ')
ax.legend()
#plt.show()
fig.savefig('polls/templates/polls/plot.png')
context = {'n':n,}
return render(request, 'polls/result.html', context)
else:
return HttpResponse("Form Not Submitted")
My result.html file where I try to get the image is:
<h1>Graph with {{n}} points</h1>
<img src='./plot.png' />
I'm running this on my localhost, is there a problem with permissions?
I've just started learning django and wanted to test this thing. Thank you for your help!
There are many things wrong about your approach but I'll try to give you some advice on how to proceed, although you may want to reconsider it.
First of all, saving your file to the templates directory won't make it available to your template. The templates directory is a special configuration that allows you to use Django's template loader, it doesn't load static files like your image.
You could save your image to static files and use the {% static %} template tag to recover it, but, again, it would be the wrong approach, as your image is not static content, it's created dynamically.
As you have no use for the data in the image after it's created, my recommendation would be to store the image in a temporary file using TempFile, or (if it's light enough) in memory using StringIO and then load those bytes in the context as base64
from StringIO import StringIO
import base64
img_in_memory = StringIO()
fig.savefig(img_in_memory, format="png") #dunno if your library can do that.
context['image'] = base64.b64encode(img_in_memory.getvalue())
Then, in your template you'd do something like:
<img src="data:image/png;base64,{{image}}" />
Or simply convert the image to string by
with open(original_image_path, "rb") as img_file:
base64_img = base64.b64encode(img_file.read()).decode('utf-8')
mimetype = mimetypes.guess_type(original_image_path)[0]
once done render this variable on HTML as
<img src="data:{{ mimetype }};base64,{{ base64_img }}"/>
It will handle both jpeg and png both.
Related
I want to generate a plot and save to memory and then pass it flask as a variable but I am stuck. I have written this code and it seems to work in google colab, when the function is called it generates the plot. However I want now to pass the variable buffer to flask render template but I am totally stuck
import io
def image_plot():
plt.figure()
my_ax = sns.violinplot(x=df_tweet["compound"])
plt.title('this is the twitter sentiment analysis for')
buffer = io.BytesIO()
my_ax.figure.savefig(buffer, format="png")
buffer.seek(0)
return buffer
return render_template("index.html", buffer=.....)
and the html part should be...
<body>
<img id="picture" src="{{ buffer }}">
</body>
I have figured out the following and it seems to work basing it this tutorial
https://buraksenol.medium.com/pass-images-to-html-without-saving-them-as-files-using-python-flask-b055f29908a
import io
import base64
plt.figure()
my_ax = sns.violinplot(x=df_tweet["compound"])
plt.title('this is saved to memory')
buffer = io.BytesIO()
my_ax.figure.savefig(buffer, format="png")
buffer.seek(0)
image_memory = base64.b64encode(buffer.getvalue())
return render_template("index.html", img_data=image_memory.decode('utf-8'))
on the html page
<img id="picture" src="data:image/png;base64,{{ img_data }}">
I am trying to develop a set of PDFs based on an HTML template using Flask package in python. Given below is the main code (runserver.py) to run the server:
from flask import Flask, render_template, make_response
import pdfkit
path_wkthmltopdf = r'C:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe'
config = pdfkit.configuration(wkhtmltopdf=path_wkthmltopdf)
options={'page-size':'A4', 'dpi':400, 'disable-smart-shrinking': ''}
app = Flask(__name__)
#app.route('/<name>/<location>')
def pdf_tempate(name, location):
rendered = render_template('pdf_template.html', name=name, location=location)
css = ['main.css']
pdf = pdfkit.from_string(rendered, False, css=css, configuration=config, options=options)
response = make_response(pdf)
response.headers['Content-Type'] = 'application/pdf'
response.headers['Content-Disposition'] = 'inline; filename=output.pdf'
return response
if __name__ == '__main__':
app.run(debug=True)
Currently, the main.css file is empty and the HTML template code is as shown below:
<html>
<head>
<title>PDF Sample</title>
</head>
<body>
<h1>Your name is : {{ name }}</h1>
<h3>Your location is : {{ location }}</h3>
</body>
</html>
The code base has the following folder structure:
|- runserver.py
|- main.css
|- templates
|-pdf_template.html
When I run the server and enter the following web address: http://127.0.0.1:5000/John/NewYork, it creates a PDF template in browser with the name and location at the appropriate locations. Till this point, everything is working as expected.
The issue with the above code is that when I need to create a loop to produce 100s of PDF from that template, it always needs to first create it in the browser to download it further. Is there any way, that I can create these PDFs and store it in a folder without the need to go into the browser?
I don't use flask, I use django. But you should be able to solve your problem by making a .zip file. I've done this when exporting tons of txt and csv files, so I'm pretty sure pdf's should work for it too (let me know).
You'll use zipfile from the Python standard library, and it should look something like the following. Obviously, replace the code from the ORM you're using, and give the proper response when a request is triggered. This is a Django example, but if you can do what you've done so far, this should be self explanatory:
import zipfile
from django.http import HttpResponse
from my_app.models import MyModel
def zip_files(request):
response = HttpResponse(content_type='application/zip')
zf = zipfile.ZipFile(response, 'w') # write mode
objects = MyModel.objects.all() # get your objects from database
for obj in objects:
contents_of_file = obj.attribute # depending on what's needed, you might want to do extra loops here
zf.writestr(obj.attribute_for_naming_file + '.pdf', obj.contents_of_file)
response['Content-Disposition'] = 'attachment; filename=my_special_zipfile.zip'
return response
UPDATE: Using Philippe Bruneau's suggestion (see below), I'm able to get the loading animation to disappear when the zip file is created. However, this method stops the zip file from being delivered to the user for download. Is there a way to have both happen? I could probably have a link show up for the user to click to download the file, but I'd really like the download to start automatically.
I'm currently writing an application that takes some API data, puts it into spreadsheets, and spits the spreadsheets out to the user in a zip file. All of that is working the way it's supposed to. Awesome.
This is my current response in views.py:
zip_file = open('locationsreport.zip', 'rb')
response = HttpResponse(zip_file, content_type='application/x-zip-compressed')
response['Content-Disposition'] = 'attachment; filename=%s' % 'locationsreport.zip'
return response
In my index.html file, I have a loading gif that shows up when the user submits the form that creates the zip file. I'd like to add a JavaScript event to the response in order to hide this gif when the zip file is returned to the user. I thought that would be formatted something like this: response = HttpResponse("<script>$('.loader').hide();</script>"). I've been looking around, and I can't seem to find a way to return both the file and the script. Is there a way to do so, or can you only return one or the other?
Could you just test in your response template if the zip_file exists ?
def myview(request):
...
context = {'zip_file':zip_file}
return render(request, 'response.html', context)
Then in your response.html template :
{% if zip_file %}
<p>Zip loaded, sorry no gif !</p>
{% else %}
<script>My gif function</script>
{% endif %}
I use Image.new create a image object in a django view,
I want to show it in webpages.
x = Image.new('RGB',(400,400))
return HttpResponse(x.show(), mimetype="image/png")
doesn't work.
How can I covert a image object to a image raw binary?
You can embed base64 images into an <img src= so you could try converting your PIL image to base64.
from PIL import Image
import StringIO
x = Image.new('RGB',(400,400))
output = StringIO.StringIO()
x.save(output, "PNG")
contents = output.getvalue().encode("base64")
output.close()
return HttpResponse('<img src="data:image/png;base64,' + contents + ' />')
You have 2 options:
Save your image in some place your web server can serve it
Encode it with base64 and show it directly
The first option is the prefered one. The code should be something like:
x = Image.new() # The same stuff than yours
x.save(MEDIA_ROOT + "/generated_images/the_name_of_the_image.jpg", "JPEG")
return HttpResponse(
"<img src="%s/%s />" % (MEDIA_URL, "/generated_images/the_name_of_the_image.jpg")
)
If you want, you can read that in base64 (see: Encoding an image file with base64)
And display it:
base64_img = get_base_64()
return HttpResponse('<img alt="Embedded Image" src="data:image/jpeg;base64," % base64_img />')
There is also a third way to accomplish this by having the view return the raw image so that the view's url can be referenced in the src attribute of the image tag.
Building on the examples others have used, the view that returns a dynamically generated image would look something like this:
from PIL import Image
import StringIO
def dynamic_image(request):
image = Image.new('RGB', (400, 400))
output = StringIO.StringIO()
image.save(output, 'PNG')
contents = output.getvalue()
output.close()
return HttpResponse(contents, content_type='image/png')
urls.py would reference this as:
urlpatterns = [
path('image', views.dynamic_image, name="dynamic_image")
]
Finally in the HTML, reference the view in the IMG tag:
<div>
<img src="{% url 'dynamic_image' %}" />
</div>
There is nothing wrong with the other approaches but they focus on returning HTML when Django views can return more than just HTML.
I had a problem before where it wouldn't show Chinese characters even when I specified #font-face to use a UTF-8 font. It turns out I cannot display images as well... so I seems like I am unable to get any of the files embeded into my pdf.
This is the code I use:
def render_to_pdf(template_src, context_dict):
"""Function to render html template into a pdf file"""
template = get_template(template_src)
context = Context(context_dict)
html = template.render(context)
result = StringIO.StringIO()
pdf = pisa.pisaDocument(StringIO.StringIO(html.encode("UTF-8")),
dest=result,
encoding='UTF-8',
link_callback=fetch_resources)
if not pdf.err:
response = http.HttpResponse(result.getvalue(), mimetype='application/pdf')
return response
return HttpResponse('We had some errors<pre>%s</pre>' % escape(html))
def fetch_resources(uri, rel):
import os.path
from django.conf import settings
path = os.path.join(
settings.STATIC_ROOT,
uri.replace(settings.STATIC_URL, ""))
return path
html
<img src="/static/images/bc_logo_bw_pdf.png" />
and
#font-face {
font-family: "Wingdings";
src: url("/static/fonts/wingdings.ttf");
}
I looked at the other quests on SO but it was no help. There are also no exceptions happening in the two functions. Also in fetch_resources function the path returned was the correct full path to the file i.e. /home/<user>/project/static/images/bc_logo_bw_pdf.png and /home/<user>/project/static/fonts/wingdings.ttf and I am at a loss as to what is wrong.
UPDATE
Everytime I create a pdf, I get this message on the console
No handlers could be found for logger "ho.pisa"
could this be related?
UPDATE #2
The font works now I made a dumb mistake... The font I was using did not have the Chinese unicode. But I still cannot embed any images onto the pdf, be it jpeg, gif or png.
I have finally solved the problem I was having... it turns out it doesn't work if I set the body's height with css... once I removed that line the image was loading perfectly...
For me (django 1.4, python 2.7 pisa==3.0.33), If I put the full path of image instead of relative, it works for me.
Try doing the same.
Everything looks better . Try once with JPG image file. In my case PNG file was also not working.
<img src="/static/images/<name>.jpg" />
without width and height attribute image will not work. add width and height attribute.
<img src="{% static 'images/logo.png' %}" alt="image" width="200" height="150" />
this fix works for me.
I have the same problem here. Don't give up with XHTML2PDF Pisa.
Pisa use PIL for generate PDF and use lib zip decoder to inserting images.
You should check if your PIL already installed properly with zip decoder, fonts and several components
I have solve this problem by installing PIL with zip decoder.
http://obroll.com/install-python-pil-python-image-library-on-ubuntu-11-10-oneiric/
If you need more detail information, you can read my article here :
http://obroll.com/how-to-load-images-files-style-css-in-pdf-using-pisa-xhtml2pdf-on-django/