Error in Downloading Files from Django app in Production with Gunicorn & Nginx

Viewed 139

I have deployed my Django app on AWS EC2 Instance using this guide.

Everything is working fine but the error arises when I download an excel file. (Excel is created using opnenpyxl library).

While downloading an xlsx file the application is returning download.htm file. Everything work great on a development server.

I tried

  1. Adding and removing the download tag. <a href="{% url 'download_estimate_excel_file' project.id project.name %}" class="dropdown-item" download> Download Excel </a>. [Didn't Work]
  2. Adding [service] Environment="LANG=ru_RU.UTF-8" to gunicorn.service file (found this on StackOverflow also). [Didn't Work]

EDIT:

Path for Gunicorn config: /etc/systemd/system/gunicorn.service

>> GUNICRON CONFIG

[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
User=sammy
Group=www-data
WorkingDirectory=/home/sammy/myprojectdir
ExecStart=/home/sammy/myprojectdir/myprojectenv/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          myproject.wsgi:application
Environment="LANG=ru_RU.UTF-8"

[Install]
WantedBy=multi-user.target

Path for Nginx config: /etc/nginx/sites-available/<myproject>

>> NGINX CONFIG

server {
    listen 80;
    server_name server_domain_or_IP;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/sammy/myprojectdir;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}
>> download_estimate_excel_file function

@login_required
def download_estimate_excel_file(request, project_id, project_name):
    project_tnc_obj = ProjectTermsAndConditions.objects.filter(
        project=project_id)
    date_time_obj = datetime.now()
    current_date = date_time_obj.strftime('%x')
    current_time = date_time_obj.strftime('%X')

    filename = project_name + '_' + str(current_date).replace('/', "-") + '_' + str(current_time).replace(":", "-") + ".xlsx"

    file_path = "media/excel/" + filename

    ...
    ...

    sheet.append([""])

    workbook.save(filename=str(file_path))
    workbook.close()
    file_ecxel = FileResponse(open(file_path, 'rb'))
    delete_file = os.remove(file_path)
    return file_ecxel

>> UPDATED download_estimate_excel_file function

@login_required
def download_estimate_excel_file(request, project_id, project_name):
    project_tnc_obj = ProjectTermsAndConditions.objects.filter(
        project=project_id)
    date_time_obj = datetime.now()
    current_date = date_time_obj.strftime('%x')
    current_time = date_time_obj.strftime('%X')

    filename = project_name + '_' + str(current_date).replace('/', "-") + '_' + str(current_time).replace(":", "-") + ".xlsx"

    file_path = "media/excel/" + filename

    ...
    ...

    sheet.append([""])

    workbook.save(filename=str(file_path))
    workbook.close()

    with open(file_path, "rb") as excel:
        data = excel.read()
    delete_file = os.remove(file_path)

    return HttpResponse(data, headers={
        'Content-Type': 'application/vnd.ms-excel',
        'Content-Disposition': 'attachment; filename= "{}"'.format(
            filename),
    })

Thanks in advance.

1 Answers

Since we don't have logging, I think it would be wise to implement some error handling. Below you'll find a perfect example from another post. Furthermore, I see you are opening, appending, and removing files from the filesystem. It is more efficient and less prone to filesystem errors to work with temporary files and save files to bytes instead of the filesystem.

from django.http import HttpResponse, HttpResponseNotFound

def waprfile(request, date):
    ...

    file_location = '/path/to/file/foo.xls'

    try:    
        with open(file_location, 'r') as f:
           file_data = f.read()

        # sending response 
        response = HttpResponse(file_data, content_type='application/vnd.ms-excel')
        response['Content-Disposition'] = 'attachment; filename="foo.xls"'

    except IOError:
        # handle file not exist case here
        response = HttpResponseNotFound('<h1>File not exist</h1>')

    return response
Related