How to save python docx on client side using ajax? - python

I use python 3.6 Django, my code looks like this:
from docx import Document
document = Document()
document.add_heading('My docx', 0)
document.save('myFile.docx')
return HttpResponse(document, content_type='application/vnd')
I don't want to save it on server, instead I want to send it to client side using ajax and save it on client pc.
Any thoughts how to do it?

I was never in touch with ajax but I know how to present your file as a download without saving it in more as a temporary buffer.
You did not present the full function or method of your code. Therefore I am giving a get() method of a class-based view as an example.
Try this:
import io
from django.http import FileResponse
def get(self):
document = Document()
document.add_heading('My docx', 0)
buffer = io.BytesIO() # create buffer
doc.save(buffer) # save doc to buffer
buffer.seek(0) # returns the curser to the beginning of the bytestream
return FileResponse(buffer, as_attachment=True, filename=f"your_file.docx")
Read more about FileResponse here.

Related

Django: how save bytes object to models.FileField?

My web application has the following structure:
backend with Django
frontend with React.
I have a form with React. I send a file from client form and I receive the file in my Django application with an APIView.
I receive a m3u file as bytes object.
b'------WebKitFormBoundaryIaAPDyj9Qrx8DrWA\r\nContent-Disposition:
form-data; name="upload";
filename="test.m3u"\r\nContent-Type: audio/x-
mpegurl\r\n\r\n#EXTM3U\n#EXTINF:-1 tvg-ID="" tvg-name="...
I would save the file in a Django model to a models.FileField and convert bytes object to m3u file.
How you do it?
models.FileField(models.ImageField) needs django.core.files.base.File like objects
ex)
django.core.files.base.File
django.core.files.images.ImageFile
File or ImageFile needs two args.
IO object : which has seek() method (ex) io.BytesIO).
name : str. (important! without name, it will not work).
bytes object doesn't have IO's methods(ex) seek()). it should be converted to IO object.
models.py
class Message(models.Model):
image = models.ImageField(upload_to='message_image/%Y/%m', null=True)
views.py or consumers.py or some-where.py
import io
from django.core.files.images import ImageFile
from myapp.models import Message
def create_image_message(image_bytes):
image = ImageFile(io.BytesIO(image_bytes), name='foo.jpg') # << the answer!
new_message = Message.objects.create(image=image)
You can try:
from django.core.files.base import ContentFile
import base64
file_data = ContentFile(base64.b64decode(fileData))
object.file.save(file_name, file_data)
You can use your file_name with an .m3u extension, and you shall have it.
I solved using temporary file. I used this code:
extM3u = str.encode(body.decode('utf8').split('EXTM3U\n#')[1].split('------WebKitFormBoundary')[0])
fileTemp = NamedTemporaryFile(delete=True, dir='media/tmp')
fileTemp.write(extM3u)
filenameRe = re.compile('.*?filename=[\'"](.*?)[\'"]')
filename = regParse(filenameRe, body.decode('utf8'))
file = File(fileTemp, name=filename)
m3u = M3u(titleField=filename, fileField=file)
m3u.save()
You can use a ContentFile. To do so:
from django.core.files.base import ContentFile
content_file = ContentFile(file_bytes, name=file_name)
# Assumes MyModel has a FileField called `file`
MyModel.objects.create(file=content_file)
convert string to bytes
bytes_data = ... # bytes
string_data = bytes_data.hex() # this is a string that you can save in TextField in django model
then, to get bytes from the string:
bytes_data_2 = bytes.fromhex(string_data)
I apologize for the crooked translation, I'm still only learning English.

Django Change File name in PDF export

I found this handy code in the Django Docs:
from reportlab.pdfgen import canvas
from django.http import HttpResponse
def some_view(request):
# Create the HttpResponse object with the appropriate PDF headers.
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="somefilename.pdf"'
# Create the PDF object, using the response object as its "file."
p = canvas.Canvas(response)
# Draw things on the PDF. Here's where the PDF generation happens.
# See the ReportLab documentation for the full list of functionality.
p.drawString(100, 100, "Hello world.")
# Close the PDF object cleanly, and we're done.
p.showPage()
p.save()
return response
I would like to pass a variable to the function as variable name, but cannot figure it out (though I am sure it is super easy).
Anyone have an idea?
Thanks so much!
filename="somefilename.pdf" there is place where you can determine your filename.
You can use it as:
filename = 'somefilename.pdf'
response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
Hope it might helps you:
Add one more argument named "name" to the view "some_view" and replace the filename with the argument value.
def hello_pdf(request, name):
# Create the HttpResponse object with the appropriate PDF headers.
response = HttpResponse(mimetype='application/pdf')
response['Content-Disposition'] = 'attachment; filename=%s.pdf'% name
temp = StringIO()
# Create the PDF object, using the StringIO object as its "file."
p = canvas.Canvas(temp)
# Draw things on the PDF. Here's where the PDF generation happens.
# See the ReportLab documentation for the full list of functionality.
p.drawString(100, 100, "Hello world.")
# Close the PDF object cleanly.
p.showPage()
p.save()
# Get the value of the StringIO buffer and write it to the response.
response.write(temp.getvalue())
return response
Check the name argument replacing the filename in the 4th line of code. Also don't forget to update the urls.py like below:
url(r'^(?P<name>\w+)/$', 'app.some_view')
After all pass the filename you wish to replace with url like below:
http://yourwebsite.com/name-of-file/
If you want to force viewing the PDF in browser with given filename but without automatic download, you can use "inline" instead of "attachment":
response["Content-Disposition"] = f'inline; filename="{file_name}"'

Flask - Handling Form File & Upload to AWS S3 without Saving to File

I am using a Flask app to receive a mutipart/form-data request with an uploaded file (a video, in this example).
I don't want to save the file in the local directory because this app will be running on a server, and saving it will slow things down.
I am trying to use the file object created by the Flask request.files[''] method, but it doesn't seem to be working.
Here is that portion of the code:
#bp.route('/video_upload', methods=['POST'])
def VideoUploadHandler():
form = request.form
video_file = request.files['video_data']
if video_file:
s3 = boto3.client('s3')
s3.upload_file(video_file.read(), S3_BUCKET, 'video.mp4')
return json.dumps('DynamoDB failure')
This returns an error:
TypeError: must be encoded string without NULL bytes, not str
on the line:
s3.upload_file(video_file.read(), S3_BUCKET, 'video.mp4')
I did get this to work by first saving the file and then accessing that saved file, so it's not an issue with catching the request file. This works:
video_file.save(form['video_id']+".mp4")
s3.upload_file(form['video_id']+".mp4", S3_BUCKET, form['video_id']+".mp4")
What would be the best method to handle this file data in memory and pass it to the s3.upload_file() method? I am using the boto3 methods here, and I am only finding examples with the filename used in the first parameter, so I'm not sure how to process this correctly using the file in memory. Thanks!
First you need to be able to access the raw data sent to Flask. This is not as easy as it seems, since you're reading a form. To be able to read the raw stream you can use flask.request.stream, which behaves similarly to StringIO. The trick here is, you cannot call request.form or request.file because accessing those attributes will load the whole stream into memory or into a file.
You'll need some extra work to extract the right part of the stream (which unfortunately I cannot help you with because it depends on how your form is made, but I'll let you experiment with this).
Finally you can use the set_contents_from_file function from boto, since upload_file does not seem to deal with file-like objects (StringIO and such).
Example code:
from boto.s3.key import Key
#bp.route('/video_upload', methods=['POST'])
def VideoUploadHandler():
# form = request.form <- Don't do that
# video_file = request.files['video_data'] <- Don't do that either
video_file_and_metadata = request.stream # This is a file-like object which does not only contain your video file
# This is what you need to implement
video_title, video_stream = extract_title_stream(video_file_and_metadata)
# Then, upload to the bucket
s3 = boto3.client('s3')
bucket = s3.create_bucket(bucket_name, location=boto.s3.connection.Location.DEFAULT)
k = Key(bucket)
k.key = video_title
k.set_contents_from_filename(video_stream)

send_file() when called return text document instead of an image

I want to send an image file from server side to client side. I am using flask framework.
But the problem is whenever I call the route in which send_file() is, the response return is an File. When I click this file gedit opens it with nothing in that file. That means it must be text file written.
I referred the flask docs for send_file().
Here is what I am doing in the code:
#app.route('/try')
def trial():
todown = 'https://igcdn-photos-e-a.akamaihd.net//hphotos-ak-xaf1//t51.2885-15//e35//12093691_1082288621781484_1524190206_n.jpg'
resp = requests.get(todown)
return send_file(resp,mimetype="image/jpeg",attachment_filename="img.jpg",as_attachment=True)
Whenever I load localhost:5000/try a file is downloaded but not the image file that I want to download.
Error I am getting in my terminal is AttributeError: 'Response' object has no attribute 'read' error.
What must be the problem. Is anything missing in this above snippet?
resp is a requests.models.Response object, not string nor bytes:
>>> import requests
>>> todown = 'https://igcdn-photos-e-a.akamaihd.net//hphotos-ak-xaf1//t51.2885-15//e35//12093691_1082288621781484_1524190206_n.jpg'
>>> resp = requests.get(todown)
>>> resp
<Response [200]>
>>> type(resp)
<class 'requests.models.Response'>
Flask.send_file() sends a file.
So first at all you need use resp.content to get the content of the object, it'll return bytes object (and by the way, resp.text return string object.
Always use .content if you're downloading an image, video, or other non-text things).
>>> import requests
>>> todown = 'https://igcdn-photos-e-a.akamaihd.net//hphotos-ak-xaf1//t51.2885-15//e35//12093691_1082288621781484_1524190206_n.jpg'
>>> resp = requests.get(todown)
>>> type(resp.content)
<class 'bytes'>
Please check the document for more details.
Then, because Flask.send_file() send a file, so you need write the image into a file before you send it.
But since you don't need use this image on your server anyway, I'd suggest use io.BytesIO in this case, then you don't need delete that image after you sent it. And note that use io.StringIO if you're sending a text file.
For example:
import requests
from io import BytesIO
from flask import Flask, send_file
app = Flask(__name__)
#app.route('/')
def tria():
todown = 'https://igcdn-photos-e-a.akamaihd.net//hphotos-ak-xaf1//t51.2885-15//e35//12093691_1082288621781484_1524190206_n.jpg'
resp = requests.get(todown)
return send_file(BytesIO(resp.content), mimetype="image/jpeg", attachment_filename="img2.jpg", as_attachment=True)
app.run(port=80, debug=True)
However, if you want write the image into a file and send it then, sure you can also do it. We can use tempfile.NamedTemporaryFile() to create a tempfile instead of just create a file to avoid rewrite your important files.
From the document:
This function operates exactly as TemporaryFile() does, except that the file is guaranteed to have a visible name in the file system (on Unix, the directory entry is not unlinked).
That name can be retrieved from the name attribute of the file object. Whether the name can be used to open the file a second time, while the named temporary file is still open, varies across platforms (it can be so used on Unix; it cannot on Windows NT or later). If delete is true (the default), the file is deleted as soon as it is closed.
The returned object is always a file-like object whose file attribute is the underlying true file object. This file-like object can be used in a with statement, just like a normal file.
For example:
import tempfile
import requests
from flask import Flask, send_file
app = Flask(__name__)
#app.route('/')
def tria():
todown = 'https://igcdn-photos-e-a.akamaihd.net//hphotos-ak-xaf1//t51.2885-15//e35//12093691_1082288621781484_1524190206_n.jpg'
resp = requests.get(todown)
with tempfile.NamedTemporaryFile() as f:
# create a file-like object use `NamedTemporaryFile()` and `with`
# as the basic usage which said in the document
f.write(resp.content)
# write the content of the image into it
return send_file(f.name, mimetype="image/jpeg",
attachment_filename="img2.jpg", as_attachment=True)
# `f.name` is the temp file's filename
app.run(port=80, debug=True)

Returning image using stream instead of static_file in Bottle

I have a simple server app written in Python using Bottle framework. At one route I create an image and write it to a stream and I want to return it as a response. I know how to return an image file using static_file function but this is costly for me since I need to write the image to a file first. I want to serve the image directly using the stream object. How can I do this?
My current code is something like this (file version):
#route('/image')
def video_image():
pi_camera.capture("image.jpg", format='jpeg')
return static_file("image.jpg",
root=".",
mimetype='image/jpg')
Instead of this, I want to do something like this:
#route('/image')
def video_image():
image_buffer = BytesIO()
pi_camera.capture(image_buffer, format='jpeg') # This works without a problem
# What to write here?
Just return the bytes. (You should also set the Content-Type header.)
#route('/image')
def video_image():
image_buffer = BytesIO()
pi_camera.capture(image_buffer, format='jpeg') # This works without a problem
image_buffer.seek(0) # this may not be needed
bytes = image_buffer.read()
response.set_header('Content-type', 'image/jpeg')
return bytes

Categories