saving blob in django creates a file with size 0 - django

I post an audio blob to django using ajax, before posting I do
console.log(blob)
//prints
//Blob(262188) {size: 262188, type: "audio/wav"}
//size: 262188
//type: "audio/wav"
//__proto__: Blob
Inside the django view:
from django.core.files.storage import default_storage
from django.core.files.base import ContentFile
audio_data = request.FILES.get("recordedBlob", None)
print(type(audio_data)) #prints <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
print(audio_data.size) #prints 262188
path = default_storage.save('audio/' + '123' + '.wav', ContentFile(audio_data.read()))
But when I open the audio file in my file system, it is size Zero Bytes.
Any idea how to save the blob correctly?

I was calling
print(len(audio_data.read()))
and it seems like calling .read() empties out the data. So taking this line away solved the problem.

Related

Django channels image saving, TextField or ImageField

Chat app using django channels
I am using websockets to send base64 encoded string to the server, the base64 encoded string could be saved in TextField or saved in ImageField by decoding using base64 library, which method is preferred, why?
EDIT
I am interested in which method is preferred and why, but not how to implement
You can use this function for converting base64 data that you get from your request into django contentfile which can be added to image model field later
import base64
from django.core.files.base import ContentFile
def base64_file(data, name=None):
_format, _img_str = data.split(';base64,')
_name, ext = _format.split('/')
if not name:
name = _name.split(":")[-1]
return ContentFile(base64.b64decode(_img_str), name='{}.{}'.format(name, ext))
# Simple example
data = request.GET.get('base64data')
data = base64_file(data, name='profile_picture')
UserProfile.profile_picture = data
UserProfile.save()

Filling MS Word Template from Django

I found some python docs relating to docxtpl at this link:
https://docxtpl.readthedocs.io/en/latest/
I followed the instruction and entered the code found at this site into a view and created the associated URL. When I go to the URL I would like for a doc to be generated - but I get an error that no HTTP response is being returned. I understand I am not defining one, but I am a bit confused about what HTTP response I need to define (I am still very new to this). The MS word template that I have saved is titled 'template.docx'.
Any help would be greatly appreciated!
VIEWS.PY
def doc_test(request):
doc = DocxTemplate("template.docx")
context = { 'ultimate_consignee' : "World company" }
doc.render(context)
doc.save("generated_doc.docx")
I would like accessing this view to generate the doc, where the variables are filled with what is defined in the context above.
Gist: Read the contents of the file and return the data in an HTTP response.
First of all, you'll have to save the file in memory so that it's easier to read. Instead of saving to a file name like doc.save("generated_doc.docx"), you'll need to save it to a file-like object.
Then read the contents of this file-like object and return it in an HTTP response.
import io
from django.http import HttpResponse
def doc_test(request):
doc = DocxTemplate("template.docx")
# ... your other code ...
doc_io = io.BytesIO() # create a file-like object
doc.save(doc_io) # save data to file-like object
doc_io.seek(0) # go to the beginning of the file-like object
response = HttpResponse(doc_io.read())
# Content-Disposition header makes a file downloadable
response["Content-Disposition"] = "attachment; filename=generated_doc.docx"
# Set the appropriate Content-Type for docx file
response["Content-Type"] = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
return response
Note: This code may or may not work because I haven't tested it. But the general principle remains the same i.e. read the contents of the file and return it in an HTTP response with appropriate headers.
So if this code doesn't work, maybe because the package you're using doesn't support writing to file-like objects or for some other reason, then it would be a good idea to ask the creator of the package or file an issue on their Github about how to read the contents of the file.
Here is a more concise solution:
import os
from io import BytesIO
from django.http import FileResponse
from docxtpl import DocxTemplate
def downloadWord(request, pk):
context = {'first_name' : 'xxx', 'sur_name': 'yyy'}
byte_io = BytesIO()
tpl = DocxTemplate(os.path.join(BASE_PATH, 'template.docx'))
tpl.render(context)
tpl.save(byte_io)
byte_io.seek(0)
return FileResponse(byte_io, as_attachment=True, filename=f'generated_{pk}.docx')

Django - Getting PIL Image save method to work with Amazon s3boto Storage

In order to resize images upon upload (using PIL), I'm overriding the save method for my Article model like so:
def save(self):
super(Article, self).save()
if self.image:
size = (160, 160)
image = Image.open(self.image)
image.thumbnail(size, Image.ANTIALIAS)
image.save(self.image.path)
This works locally but in production I get an error:
NotImplementedError: This backend doesn't support absolute paths.
I tried replacing the image.save line with
image.save(self.image.url)
but then I get an IOError:
[Errno 2] No such file or directory: 'https://my_bucket_name.s3.amazonaws.com/article/article_images/2.jpg'
That is the correct location of the image though. If I put that address in the browser, the image is there. I tried a number of other things but so far, no luck.
You should try and avoid saving to absolute paths; there is a File Storage API which abstracts these types of operations for you.
Looking at the PIL Documentation, it appears that the save() function supports passing a file-like object instead of a path.
I'm not in an environment where I can test this code, but I believe you would need to do something like this instead of your last line:
from django.core.files.storage import default_storage as storage
fh = storage.open(self.image.name, "w")
format = 'png' # You need to set the correct image format here
image.save(fh, format)
fh.close()
For me default.storage.write() did not work, image.save() did not work, this one worked. See this code if anyone is still interested. I apologize for the indentation. My project was using Cloudinary and Django small project.
from io import BytesIO
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage as storage
def save(self, *args, **kargs):
super(User, self).save(*args, **kargs)
# After save, read the file
image_read = storage.open(self.profile_image.name, "r")
image = Image.open(image_read)
if image.height > 200 or image.width > 200:
size = 200, 200
# Create a buffer to hold the bytes
imageBuffer = BytesIO()
# Resize
image.thumbnail(size, Image.ANTIALIAS)
# Save the image as jpeg to the buffer
image.save(imageBuffer, image.format)
# Check whether it is resized
image.show()
# Save the modified image
user = User.objects.get(pk=self.pk)
user.profile_image.save(self.profile_image.name, ContentFile(imageBuffer.getvalue()))
image_read = storage.open(user.profile_image.name, "r")
image = Image.open(image_read)
image.show()
image_read.close()
If you are working with cloud storages for files in Django
NotImplementedError: This backend doesn't support absolute paths
To fix it you need to replace file.path with file.name
For code in the the question: image.save(self.image.path) with image.save(self.image.name)
Here how it looks like in the console
>>> c = ContactImport.objects.last()
>>> c.json_file.name
'protected/json_files/data_SbLN1MpVGetUiN_uodPnd9yE2prgeTVTYKZ.json'
>>> c.json_file
<FieldFile: protected/json_files/data_SbLN1MpVGetUiN_uodPnd9yE2prgeTVTYKZ.json>
>>> c.json_file.url
'https://storage.googleapis.com/super-secret/media/api/protected/json_files/data_SbLN1MpVGetUiN_uodPnd9yE2prgeTVTYKZ.json?Expires=1631378947&GoogleAccessId=secret&Signature=ga7...'

Django admin upload and image to s3 and then resize the image and save a thumb problem

I am having error after error trying to upload and resize images to s3 with pil and botos3 and the django default_storage. I am trying to do this on save in the admin.
here is the code:
from django.db import models
from django.forms import CheckboxSelectMultiple
import tempfile
from django.conf import settings
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage as s3_storage
from django.core.cache import cache
from datetime import datetime
import Image, os
import PIL.Image as PIL
import re, os, sys, urlparse
class screenshot(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200)
image = models.ImageField(upload_to='screenshots')
thumbnail = models.ImageField(upload_to='screenshots-thumbs', blank=True, null=True, editable=False)
def save(self):
super(screenshot, self).save() # Call the "real" save() method
if self.image:
thumb = Image.open(self.image.path)
thumb.thumbnail(100, 100)
filename = str(self.slug)
temp_image = open(os.path.join('tmp',filename), 'w')
thumb.save(temp_image, 'JPEG')
from django.core.files import File
thumb_data = open(os.path.join('/tmp',filename), 'r')
thumb_file = File(thumb_data)
new_file.thumb.save(str(self.slug) + '.jpg', thumb_file)
def __str__(self):
return self.title
This is just one of the many ways I have tried to get it working, and I either get (2, 'No such file or directory') or some other error.
Please can someone help me to get it working. I want it to use the django backend to get the image uploaded to be resized and saved as the thumbnail and then saved. Let me know if you need to know any information. I would be happy to use the django snippet - http://djangosnippets.org/snippets/224/ but I don't know what data to feed it. I get the same IOErrors and 'no such path/filename' even though the main image is uploading to s3 fine. I have also tried things like:
myimage = open(settings.MEDIA_URL + str(self.image))
myimage_io = StringIO.StringIO()
imageresize = myimage.resize((100,100), Image.ANTIALIAS)
imageresize.save('resize_100_100_aa.jpg', 'JPEG', quality=75)
It's been 3 days of looking now so I am starting to go spare! Thanks
I had a similar problem, but in my case using sorl-thumbnail was not an option. I found that I can open an Image directly from S3BotoStorage by passing in a file descriptor instead of a path.
So instead of
thumb = Image.open(self.image.path)
use
thumb = Image.open(s3_storage.open(self.image.name))
Then you can process and save the new file locally as you were doing before.
Why don't you try sorl-thumbnail. It has the exact same interface as the default ImageField django provides and it seems like it would be a lot nicer to work with than the roll-your-own support.
Storage support
Pluggable Engine support (PIL, pgmagick)
Pluggable Key Value Store support (redis, cached db)
Pluggable Backend support
Admin integration with possibility to delete
Dummy generation
Flexible, simple syntax, generates no html
ImageField for model that deletes thumbnails
CSS style cropping options
Margin calculation for vertical positioning

How to open a generated PDF file in browser?

I have written a Pdf merger which merges an original file with a watermark.
What I want to do now is to open 'document-output.pdf' file in the browser by a Django view. I already checked Django's related articles, but since my approach is relatively different, I don't directly create the PDF object, using the response object as its "file.", so I am kind of lost.
So, how can I do is in a Django view?
from pyPdf import PdfFileWriter, PdfFileReader
from reportlab.pdfgen.canvas import Canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
output = PdfFileWriter()
input = PdfFileReader(file('file.pdf', 'rb'))
# get number of pages
num_pages = input.getNumPages()
# register new chinese font
pdfmetrics.registerFont(TTFont('chinese_font','/usr/share/fonts/truetype/mac/LiHeiPro.ttf'))
# generate watermark on the fly
pdf = Canvas("watermark.pdf")
pdf.setFont("chinese_font", 12)
pdf.setStrokeColorRGB(0.5, 1, 0)
pdf.drawString(10, 830, "你好")
pdf.save()
# put on watermark
watermark = PdfFileReader(file('watermark.pdf', 'rb'))
page1 = input.getPage(0)
page1.mergePage(watermark.getPage(0))
# add processed pdf page
output.addPage(page1)
# then, add rest of pages
for num in range(1, num_pages):
output.addPage(input.getPage(num))
outputStream = file("document-output.pdf", "wb")
output.write(outputStream)
outputStream.close()
I know its an older post but we can use the embed tag of html to implement this kind of functionality. For e.g.:
<embed height="100%" width="100%" name="plugin" src="filename.pdf" type="application/pdf">
So in your case, you can simply send the response using render to response as:
return render_to_response("abc.html",{"filename":filename})
and in the abc.html you can put this filename (with the path) in the embed tag, as mentioned above.
Hope this helps.
In addition to sending your PDF back to the browser, you can also save some cycles by storing your watermark in a string buffer.
from pyPdf import PdfFileWriter, PdfFileReader
from reportlab.pdfgen.canvas import Canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from django.http import HttpResponse
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
def some_view(request):
output = PdfFileWriter()
input = PdfFileReader(file('file.pdf', 'rb'))
#create response object
response = HttpResponse(mimetype='application/pdf')
response['Content-Disposition'] = 'attachment; filename=somefilename.pdf'
# get number of pages
num_pages = input.getNumPages()
#register the font
pdfmetrics.registerFont(TTFont('chinese_font','/usr/share/fonts/truetype/mac/LiHeiPro.ttf'))
# generate watermark on the fly
buffer = StringIO() # create string buffer for PDF
pdf = Canvas(buffer)
pdf.setFont("chinese_font", 12)
pdf.setStrokeColorRGB(0.5, 1, 0)
pdf.drawString(96, 26, "88888")
pdf.save()
# put on watermark from buffer
watermark = PdfFileReader(buffer)
page1 = input.getPage(0)
page1.mergePage(watermark.getPage(0))
# add processed pdf page
output.addPage(page1)
#stream to browser
outputStream = response
output.write(response)
outputStream.close()
return response
I am not sure I follow. If you want the PDF content to be sent to the browser you should use an HttpResponse instance. This line in your code
outputStream = file("document-output.pdf", "wb")
will not serve to write the PDF contents to the response. Instead it looks to me like it will write the contents to a local file, which is not the same.
Update
Based on comment:
How to send PDF content to a HttpResponse object as it will open in the browser, not as an attachment.
AFAIK (if anyone knows better, correct me) this is browser dependent.
If you leave out the Content-Disposition = "attachment; filename=foo.pdf from the response headers you can send the contents to the browser without a specific filename. This prompted my Firefox browser (3.6.10, Ubuntu Jaunty) to ask me if I wanted to open it using a program. On Chrome (6.0.472.62, Ubuntu Jaunty) the file got downloaded as download.pdf without any prompting.
remove 'attachment' from this line with Chris comment
response['Content-Disposition'] = 'attachment; filename=somefilename.pdf'