Tahreem Iqbal
Tahreem Iqbal

Reputation: 1015

Generate pdf files with Weasyprint, save in zip file, send that zip file to client and present it for download

Let me break down my requirement. Here's what I'm doing right now.

1. Generate PDF files from HTML

for this I'm using Weasyprint as following:

lstFileNames = []
for i, content in enumerate(lstHtmlContent):
    repName = 'report'+ str(uuid.uuid4()) + '.pdf'
    lstFileNames.append("D:/Python/Workspace/" + repName)

    HTML(string=content).write_pdf(target=repName,
        stylesheets=[CSS(filename='/css/bootstrap.css')])

all files names, with paths, are saved in lstFileNames.

2. Create a zip file with pdf files generated by weasyprint

for this I'm using zipfile

zipPath = 'reportDir' + str(uuid.uuid4()) + '.zip'
myzip = zipfile.ZipFile(zipPath, 'w') 
with  myzip:
    for f in lstFileNames:
        myzip.write(f)

3. Send zip file to client for download

resp = HttpResponse(myzip, content_type = "application/x-zip-compressed")

resp['Content-Disposition'] = 'attachment; filename=%s' % 'myzip.zip'

4. Open file for downloading via Javascript

var file = new Blob([response], {type: 'application/x-zip-compressed'});
var fileURL = URL.createObjectURL(file);
window.open(fileURL);

Problems

1. While the zip file is successfully received at front end, after I try to open it, it gives the following error:

The archive is in either unknown format or damaged

Am I sending the file wrong or is my Javascript code the problem?

2. Is there a way to store all pdf files in list of byte arrays and generate zip files with those byte array and send it to the client? I tried that with weasyprint but the result was same damaged file.

3. Not exactly a problem but I haven't been able to find it in weasyprint docs. Can I enforce the path to where the file should be saved?

Problem # 1 is of extreme priority, rest are secondary. I would like to know if I'm doing it right i.e. generating pdf files and sending their zip file to client.

Thanks in advance.

Upvotes: 0

Views: 2513

Answers (2)

Simon Hänisch
Simon Hänisch

Reputation: 4968

A slightly different approach would be to move the zip file to a public directory and then send that location to the client (e.g. json formatted), i.e.:

publicPath = os.path.join('public/', os.path.basename(zipPath))
os.rename(zipPath, os.path.join('/var/www/', publicPath))

jsonResp = '{ "zip-location": "' + publicPath + '" }'

resp = HttpResponse(jsonResp, content_type = 'application/json');

Then in your client's javascript:

var res = JSON.parse(response);
var zipFileUrl = '/' + res['zip-location'];

window.open(zipFileUrl, '_blank');

'/' + res['zip-location'] assumes that your page lives in the same folder as the public directory (so http://example.com/public/pdf-files-123.zip points to /var/www/public/pdf-files-123.zip on your file system).

You can clean up the public directory with a cron job that deletes all the .zip files in there that are older than an hour or so.

Upvotes: 1

Alasdair
Alasdair

Reputation: 308909

Once you have exited the with block the filehandle is closed. You should reopen the file (this time with open) and use read() to pass the contents to HttpResponse instead of passing the filehandle itself.

with zipfile.ZipFile(zipPath, 'w') as myzip
    for f in lstFileNames:
        myzip.write(f)
with open(zipPath, 'r') as myzip:
    return HttpResponse(myzip.read(), content_type = "application/x-zip-compressed")

If that works, then you can use a StringIO instance instead of a filehandle to store the zip file. I'm not familiar with Weasyprint so I don't know whether you can use StringIO for that.

Upvotes: 0

Related Questions