2

My django web app makes and save docx and I need to make it downloadable. I use simple render_to_response as below.

return render_to_response("test.docx", mimetype='application/vnd.ms-word')

However, it raises error like 'utf8' codec can't decode byte 0xeb in position 15: invalid continuation byte

I couldn't serve this file as static so I need to find a way to serve it as this. Really appreciate for any help.

brsbilgic
  • 10,643
  • 12
  • 55
  • 89
  • Maybe this will help [link](http://stackoverflow.com/questions/5552555/unicodedecodeerror-invalid-continuation-byte) – dydek Oct 16 '13 at 09:54

5 Answers5

11

Yep, a cleaner options, as stated by wardk would be, using https://python-docx.readthedocs.org/:

from docx import Document
from django.http import HttpResponse

def download_docx(request):
    document = Document()
    document.add_heading('Document Title', 0)

    response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document')
    response['Content-Disposition'] = 'attachment; filename=download.docx'
    document.save(response)

    return response
Vitor Freitas
  • 3,000
  • 1
  • 19
  • 31
  • 1
    This is a great solution especially as the file does not need to be saved to a drive. – orbital Jan 21 '16 at 20:29
  • Not sure, but don't you need to add document inside the HttpResponse, i.e. `response = HttpResponse(document, content_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document')`. – lordB8r Dec 14 '16 at 18:41
7

I managed to generate a docx document from a django view thanks to python-docx.

Here is an example. I hope it helps

from django.http import HttpResponse
from docx import Document
from cStringIO import StringIO

def your_view(request):
    document = Document()
    document.add_heading(u"My title", 0)
    # add more things to your document with python-docx

    f = StringIO()
    document.save(f)
    length = f.tell()
    f.seek(0)
    response = HttpResponse(
        f.getvalue(),
        content_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document'
    )
    response['Content-Disposition'] = 'attachment; filename=example.docx'
    response['Content-Length'] = length
    return response
luc
  • 37,543
  • 21
  • 117
  • 168
1

Try with this response:

response = HttpResponse(mydata, mimetype='application/vnd.ms-word')
response['Content-Disposition'] = 'attachment; filename=example.doc'
return response 
fasouto
  • 3,877
  • 2
  • 25
  • 60
1

Is it possible, that your path to 'test.docx' contains non-ascii-characters? Did you check all local variables on the django debug page?

What I did to download an xml file was not to create the file on disc but to use a memory file (saves me from dealing with file systems, path, ...):

    memory_file = StringIO.StringIO()
    memory_file.writelines(out) #out is an XMLSerializer object in m case

    response = HttpResponse(memory_file.getvalue(), content_type='application/xml')
    response['Content-Disposition'] = 'attachment; filename="my_file.xml"'
    response['Content-Length'] = memory_file.tell()
    return response

Maybe you can adapt this to your docx-situation.

OBu
  • 4,261
  • 2
  • 23
  • 41
1

For Downloading a DOCX file that Already Exists

I slightly modified @luc's answer. His post got me 98% of the way. I made changes because I did not need to create a Word file. My usage was simply to pass one that existed on the fileserver and then ready it for downloading.

Because the doc for downloading already exists.

def download(request):
filepath = os.path.abspath(r"path\to\file.docx")
print('SLA FILE: ', filepath)
if os.path.exists(filepath):
    with open(filepath, 'rb') as worddoc: # read as binary
        content = worddoc.read() # Read the file
        response = HttpResponse(
            content,
            content_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document'
        )
        response['Content-Disposition'] = 'attachment; filename=download_filename.docx'
        response['Content-Length'] = len(content) #calculate length of content
        return response
else:
    return HttpResponse("Failed to Download SLA")
Andrew
  • 1,124
  • 1
  • 13
  • 24