Django serve a PDF document loaded from Mongodb - python

I am storing PDF documents in MongoDB; base64 encoded. I want to serve these PDFs from a view function. I'm hoping to eventually embed them into an HTML embed element or Iframe. For now, I'm just trying to get this to work.
Similar questions:
django PDF FileResponse "Failed to load PDF document." (OP forgot to call .seek on the buffer)
Django FileResponse PDF - pdf font changes in frontend - (Django DRF and React.js) (no answer yet)
how to display base64 encoded pdf? (answers don't involved Django)
Here is my view:
def pdf(request, pdf_id):
document = mongo_db_client.get_document(pdf_id) # uses a find_one query, returns a cursor on the document
pdf = base64.b64decode(document.read())
print(f"pdf type: {type(pdf)}")
print(f"pdf length: {len(pdf)}")
# We save the PDF to the filesystem to check
# That at least that works:
with open("loaded_pdf.pdf", "wb") as f:
f.write(pdf)
# See: https://docs.djangoproject.com/en/4.0/howto/outputting-pdf/
_buffer = io.BytesIO(pdf)
p = canvas.Canvas(_buffer)
p.showPage()
p.save()
_buffer.seek(0)
return FileResponse(_buffer, content_type='application/pdf')
The output of this is that I am able to view the PDF saved to the filesystem and the print output is:
pdf type: <class 'bytes'>
pdf length: 669764
Now, for one of the PDFs that I have, I can open the URL and view it in the browser. For another PDF that I have, it fails to load in the browser, showing only the PDF title. In both cases, the PDF saved to the filesystem and I can view it there without error.
Any ideas?

Related

Open an image in python and upload it to S3

I was trying to open a file/image in python/django and upload it to s3 but I get different errors depending on what I try. I can get it to work when I send the image using the front end html form but not when opening the file on the back end. I get errors such as "'bytes' object has no attribute 'file'" Any ideas how to open an image and upload it to s3? I wasn't sure if I was using the correct upload function, but it worked when I received the file from an html form instead of opening it directly.
image = open(fileURL, encoding="utf-8")
S3_BUCKET = settings.AWS_BUCKET
session = boto3.Session(
aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
)
s3 = session.resource('s3')
s3.Bucket(S3_BUCKET).put_object(Key='folder/%s' % fileName, Body=image)
Thanks.
The open command return a file object. Therefore Body=image does not contain the actual contents of the object.
Since you want to upload an existing object, you could use:
Key = 'folder/' + fileName
s3.Object(S3_BUCKET, Key).upload_file(fileURL)

how to write svg code to a file and save It in media folder in Django?

I have a function that generates a barcode as svg code.
I need to display this svg in an HTML PDF template. but unfortunately inline svg doesn't work.
so as a workaround I need to write this code into a file and save it in the media folder so I can access it by tag in the HTML PDF template.
Below is the function that generates the barcode:
def barcode_number(self):
number = self.get_real_instance().number
svg_code = generate('code39', str(number).zfill(20), pil=True)
code = svg_code.decode("utf-8")
## here I want to write code into barcode.svg file
safe_html = mark_safe(svg_code)
return safe_html
How can I write this code to a svg file and save it in '/media/uploads' ?
Thank you

'Download simulation data' link on html using flask app

I am reading user inputs and sending them for processing. After processing, the results are displayed. Along with the results I want a link on webpage to be able to download the data as a csv file. My function to process inputs looks as follows.
#app.route('/process', methods=['POST'])
def process_data():
# create csv_file
return render_template("results.html", data=csv_file)
results.html has following line.
<p> <large>Download simulation data </large></p>
This link is correctly displayed on the webpage. My function to download the data looks as follows.
#app.route('/download/<filename>')
def download(filename):
response = make_response(filename)
response.headers["Content-Disposition"] = "attachment; filename=Simulation.csv"
response.headers["Content-Type"] = "text/csv"
return response
Clicking the download link, I get '414 Request-URI Too Large'.
Is there a better solution for passing data from flask to html to flask again?
I can see that my entire data are appended to url, can I somehow avoid that?
Is it possible to directly pass response while rendering results.html and make it downloadable?
UPDATE
I learnt that putting data in url is a bad idea. Instead I can use dataurl by encoding the data and then use dataurl for csv in href tag of html.
buffer = StringIO()
dataframe.to_csv(buffer, index=False)
buffer.seek(0)
data_str = base64.b64encode(buffer.getvalue().encode('utf8')).decode('ascii')
url = "data:text/csv; base64,{}".format(data_str)
html looks like as follows.
<a download="SSW_Simulation.csv" href="{{ data_url }}">download</a>
However this solution does not work in internet explorer I guess because urls for data are not supported. Should I be saving the csv file somewhere and pass that filename to html? so that when prompted, I can fetch it from the temporary location and download using make_response? I would prefer not to save the file to the disk.
Handling the data in javascript solved the problem as suggested by #Jeronimo. json string was passed to html page.
import json
#app.route('/process', methods=['POST'])
def process_data():
# create csv_file
data = json.dumps(csv_file)
return render_template("results.html", data=data)
Javascript suggested in this answer was added to html and along with download button.
<button onclick="download({{ data }}, 'myfile.csv', 'text/csv')">download data</button>

Return dynamically generated zip file as json response when download button is clicked

I am dynamically generating zip file with result after user searches for data. I want to return the zip file as a json response and allow user to download it when download button is clicked.
Below is the code i have used which returns HttpResponse. I want to convert this to a json response. I tried a few approaches and it didn't work. I am not sure how the url should be specified in the template and also the url pattern for the same.
Below is the code I have used in the view:
filename = "{}_{}.txt".format(query_word, doc_id)
zip_dir = "result_for_query_{}.zip".format(query_word)
z = zipfile.ZipFile(zip_dir, 'a')
self.write_to_file(filename, doc, z)
z.close()
response = HttpResponse(z,content_type='application/zip')
response["Content-Disposition"] = "attachment; filename=%s" % zip_dir
return response
And my template:
Download
I am new to programming. So, I am still learning how to write urlpatterns.

"IOError: cannot identify image file" while saving file content to ImageField in Django

How to save generated file content to ImageField in Django?
My current code is:
from django.contrib.contenttypes.models import ContentType
image = create_screenshot(url)
print type(image) # <type 'str'>
screenshot = ScreenshotModel(...)
screenshot.image.save('filename.jpg', ContentFile(image), save=True)
This produces no errors while saving, but gives error while trying to show image (using sorl.thumbnail):
IOError: cannot identify image file <cStringIO.StringI object at 0x7fda5322c140>
File is being saved on disk properly, but it is not image and I cannot display it. I have checked file mime type and it is application/octet-stream.
Function create_screenshot generates image using PhantomJS and returns raw output of phantomjs script - when I use it to generate PDF documents and saving it to FileField everything is ok.

Categories

Resources