Serving .json file to download - python

I am trying to serve a .json file through this function. The problem is that every time I make the request the browser displays the content instead of downloading the file.
I think it could be due to the fact that I am using .read() as a parameter for the HttpResponse object constructor. However, if I use only the file object, I get the following exception:
TypeError: cannot serialize '_io.BufferedRandom' object
Code
try:
invoices = models.Invoice.objects.filter(pk__in=document_ids).order_by(*ordering)
pcustomers = models.CustomerProxy.objects.all()
mixed_query = list(invoices) + list(pcustomers)
file = tempfile.NamedTemporaryFile(suffix='.json')
file.write(serializers.serialize('json', mixed_query).encode())
file.seek(0)
response = HttpResponse(file.read(), content_type='application/json')
response['Content-Disposition'] = 'attachment; filename=%s' % file.name
response['Content-Length'] = os.path.getsize(file.name)
except Exception:
raise
return response

You don't need to go through the whole file generation process to create a downloadable file, you just need to add the Content-Disposition header normally. Does the code below work?
...
mixed_query = list(invoices) + list(pcustomers)
json_str = serializers.serialize('json', mixed_query))
response = HttpResponse(json_str, content_type='application/json')
response['Content-Disposition'] = 'attachment; filename=export.json'

Based on the code that you show, you do not need to write to a temporary file. Why don't you just pass the result of serialize() into HttpResponse()?
response = HttpResponse(serializers.serialize('json', mixed_query), content_type='application/json')
You can set the attachment name to whatever you like, something descriptive would seem to be better than the random alphanumeric string generated by tempfile.NamedTemporaryFile().
response['Content-Disposition'] = 'attachment; filename="invoices_and_customers.json"'
If you really want to specify the length:
response['Content-Length'] = len(response.content)
or you could add the ConditionalGetMiddleware middleware to your settings and have Django add the Content-Length for you.

add this to your Http response
HttpResponse(mimetype='application/force-download')

Related

Empty download file from django view

I am using django 1.11.2 and I want to have view with downloadable file. After click on link the file is downloading but file is empty and instead of png, I recieved empty txt file.
def download_file(request, uuid):
response = HttpResponse(content_type='application/force-download')
response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(models.DownloadLink.objects.get(uuid=uuid).download_file.file)
response['Content-Length'] = 'http://{}{}'.format(Site.objects.get_current(), models.DownloadLink.objects.get(uuid=uuid).download_file.file.url)
return response
EDIT:
value here response['Content-Length'] is http://127.0.0.1:8000/media/filer_public/49/54/4954a7bb-8ad3-4679-9248-bffc7d186ca8/photo-105221.jpeg
Just pass the file in HttpResponse constructor like this:
def download_file(request, uuid):
file = models.DownloadLink.objects.get(uuid=uuid).download_file
response = HttpResponse(file.file, content_type='application/force-download')
response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(file.file)
response['Content-Length'] = 'http://{}{}'.format(Site.objects.get_current(), file.file.url)
return response
From HttpResponse docs
content should be an iterator or a string. If it’s an iterator, it
should return strings, and those strings will be joined together to
form the content of the response. If it is not an iterator or a
string, it will be converted to a string when accessed.

Django Serving a Download File

I'm trying to serve a txt file generated with some content and i am having some issues. I'vecreated the temp files and written the content using NamedTemporaryFile and just set delete to false to debug however the downloaded file does not contain anything.
My guess is the response values are not pointed to the correct file, hense nothing is being downloaded, heres my code:
f = NamedTemporaryFile()
f.write(p.body)
response = HttpResponse(FileWrapper(f), mimetype='application/force-download')
response['Content-Disposition'] = 'attachment; filename=test-%s.txt' % p.uuid
response['X-Sendfile'] = f.name
Have you considered just sending p.body through the response like this:
response = HttpResponse(mimetype='text/plain')
response['Content-Disposition'] = 'attachment; filename="%s.txt"' % p.uuid
response.write(p.body)
XSend requires the path to the file in
response['X-Sendfile']
So, you can do
response['X-Sendfile'] = smart_str(path_to_file)
Here, path_to_file is the full path to the file (not just the name of the file)
Checkout this django-snippet
There can be several problems with your approach:
file content does not have to be flushed, add f.flush() as mentioned in comment above
NamedTemporaryFile is deleted on closing, what might happen just as you exit your function, so the webserver has no chance to pick it up
temporary file name might be out of paths which web server is configured to send using X-Sendfile
Maybe it would be better to use StreamingHttpResponse instead of creating temporary files and X-Sendfile...
import urllib2;
url ="http://chart.apis.google.com/chart?cht=qr&chs=300x300&chl=s&chld=H|0";
opener = urllib2.urlopen(url);
mimetype = "application/octet-stream"
response = HttpResponse(opener.read(), mimetype=mimetype)
response["Content-Disposition"]= "attachment; filename=aktel.png"
return response

How can i make the common function/view for servinf downloadble files in django python

I am displaying the list of objects in the html table.
i have the download link in front of every row which i want them to download the linked file.
I have made this function
def make_downloadable_link(path):
#Prepare the form for downloading
wrapper = FileWrapper(open(mypath))
response = HttpResponse(wrapper,'application/pdf')
response['Content-Length'] = os.path.getsize(mypath)
fname = mypath.split('/')[-1]
response['Content-Disposition'] = 'attachment; filename= fname'
return response
This is working fine if i use it for hard coded path in view for single file. But i want to make a generic view so that it works on all the files in the table
I hav the path of the file avaiable in object.path variable but i am confused how can i pass the path object to the downlaod file view. because i want to hide that actual path from the user.
I don't know what to write in the URLs.py file fo that download file view
What would you like to do is get actual file path from object. And as you have said the file path is stored in object.path that makes it easy.
For example:
urls.py
url(r'^download/(?P<object_id>\d+)/$', "yourapp.views.make_downloadable_link", name="downloadable")
In views.py:
def make_downloadable_link(object_id):
# get object from object_id
object = ObjectModel.objects.get(id=object_id)
mypath = object.path
#prepare to serve the file
wrapper = FileWrapper(open(mypath))
response = HttpResponse(wrapper,'application/pdf')
response['Content-Length'] = os.path.getsize(mypath)
fname = mypath.split('/')[-1]
response['Content-Disposition'] = 'attachment; filename= fname'
return response

How to download a file uploaded using django-filebrowser?

I am trying to create a download of a file object. the file was added using django-filebrowser which means it is turn in to a string path to the the file. I have tried the following:
f = Obj.objects.get(id=obj_id)
myfile = FileObject(os.path.join(MEDIA_ROOT, f.Audio.path))
...
response = HttpResponse(myfile, content_type="audio/mpeg")
response['Content-Disposition'] = 'attachment; filename=myfile.mp3'
return response
The file that is downloaded contains the string of the path to the file location and not the file. Could anyone be of assistance on how to access the file object?
f = Obj.objects.get(id=obj_id)
myfile = open(os.path.join(MEDIA_ROOT, f.Audio.path)).read()
...
response = HttpResponse(myfile, content_type="audio/mpeg")
response['Content-Disposition'] = 'attachment; filename=myfile.mp3'
return response
NOTE! This is not memory friendly! Since the whole file is put into memory. You're better of using a webserver for file serving or if you want to use Django for file serving you could use xsendfile or have a look at this thread
You need to open the file and send it's binary contents back in the response. So something like:
fileObject = FileObject(os.path.join(MEDIA_ROOT, f.Audio.path))
myfile = open(fileObject.path)
response = HttpResponse(myfile.read(), mimetype="audio/mpeg")
response['Content-Disposition'] = 'attachment; filename=myfile.mp3'
return response
Hope that gets what you're looking for.

How to save file data from POST variable and load it back to response in Python Django?

I have a such problem - I am using Python 2.6 / Django 1.3 and I need to accept as POST variable with key 'f', which contains a binary data. After that, I need to save data in a file.
POST
T$topX$objectsX$versionY$archiverО©ҐR$0О©ҐО©ҐО©Ґull_=<---------------------- content of file -------------------->О©ҐО©Ґ_NSKeyedArchive(258:=CО©ҐО©Ґ
Code
from django.core.files.storage import default_storage
from django.core.files.base import ContentFile
def save(request):
upload_file = request.POST['f']
save_path = default_storage.save('%s%s' % (save_dir, filename),
ContentFile(upload_file))
When I am trying to do
nano /tmp/myfile.zip
It returns data like
T^#^#^#$^#^#^#t^#^#^#o^#^#^#p^#^#^#X^#^#^#$^#^#^#o^#^#^#b^#^#^#j^#^#^#e^#^#^#c^#^#^#t^#^#^#s^#^#^#X^#^#^#$^#^#^#v^#^#^#e^#^#^#r^#^#^#s^#^#^#i^#^#$
When its done, I am going to read saved file
def read(request):
user_file = default_storage.open(file_path).read()
file_name = get_filename(file_path)
response = HttpResponse(user_file, content_type = 'text/plain',
mimetype = 'application/force-download')
response['Content-Disposition'] = 'attachment; filename=%s' % file_name
response['Content-Length'] = default_storage.size(file_path)
return response
In case, when I am writing
print user_file
It returns a correct data, but when I am returning a HttpResponse it has a different data from a source
It would probably be easier, and more memory efficient if you just save the data into a file, and like #keckse said, let a browser stream it. Django is very inefficient in streaming data. It will all depend on the size of the data. If you want to stream it with django anyways, it can be done like this:
from django.http import HttpResponse
import os.path
import mimetypes
def stream(request, document, type=None):
doc = Document.objects.get(pk=document)
fsock = open(doc.file.path,"r")
file_name = os.path.basename(doc.file.path)
mime_type_guess = mimetypes.guess_type(file_name)
if mime_type_guess is not None:
response = HttpResponse(fsock, mimetype=mime_type_guess[0])
response['Content-Disposition'] = 'attachment; filename=' + file_name
return response
In your case you might want to set the mime type manually, you can try out application/octet-stream too. The mainpassing iterators difference is that you pass the "string" from file.read(), instead of the handle to the file directly. Please note: if you use read(), you will be loading the whole file into memory.
More on passing iterators to HttpResonse. And I might be wrong, but I think you can drop the content-type.

Categories