skip to Main Content

I want to be able to download files on my Django website by clicking on a link. But even by following some tutorials it’s not working. So first, I have a function that is generating files when I click on a button. This function is working fine, this is not the problem. Those files are being displayed on the page, and this is also working fine. The problem is about the downloading part. The page that is displaying the file names as links

This is the function that is rendering the page and that is returning a list with all the files in a directory. And then the other part of this function is supposed to let me download the files by clicking their link.

# views.py
def print_ip(request, filename=''):
    if request.method == "POST":
        get_ipfile(request)

    ip_files_list = os.listdir("mysite/ip_print_files/")

    if filename != '':
        filepath = "mysite/ip_print_files/" + filename
        path = open(filepath, 'rb')
        mime_type, _ = mimetypes.guess_type(filepath)
        response = HttpResponse(path, content_type=mime_type)
        response['Content-Disposition'] = "attachment; filename=%s" % filename
        return response
    else:
        return render(request, "print_ip.html", {"files": ip_files_list})

And this is the HTML file of the page shown in the previous image. As you can see in the image above, the file names are being displayed correctly but I’m stuck for the part of being able to use the links.

{% extends "layout.html" %}

{% block title %}
    Print Ip
{% endblock %}

{% block main %}
    <h1>Print Ip</h1>
    {% for file in files %}
        <p><a href="{{ file }}">{{ file }}</a></p>
    {% endfor %}
    
    <form method="POST">
        {% csrf_token %}
        <input type="submit" value="Print Ip Now">
    </form>
    

{% endblock %}

This is the code from the tutorial I followed.

3

Answers


  1. Chosen as BEST ANSWER

    Ok thank you all. So I think like you said @Bruno it's not possible to take file from the server and download it. Or if it's possible it's too complicated. Again, thank you all for your time.

    I think we should close this question.


  2. First of all as @Marat pointed out your parameter filename='' is never changed in the function, therefor the block if filename != '': is never executed. Try if filename == '': instead.

    Not sure if below is an answer to your question, but it shows how to download a file from the browser in Django.

    To select a file in a template you can do this with <input type="file" .../>, note the form needs to have the statement enctype="multipart/form-data".

    The file is then posted in the view with request.FILES['file_name']. Once you have it you can do whatever you want, in the example below I display the file in the same tab. Note in the example this only works for pdf documents.

    A minimal example of file selection and display in the browser is below.

    view_select_file.py

    from django.shortcuts import render
    from django.http import FileResponse
    from django.utils.datastructures import MultiValueDictKeyError
    
    def view_select_file(request):
        if request.method == "POST":
            try:
                file_name = request.FILES['file_name']
    
            except MultiValueDictKeyError:
                return render(request, "select_file_template.html")
                
            # do something with the file for example display in another tab
            return FileResponse(file_name, content_type='application/pdf',as_attachment=True)
    
        return render(request, "select_file_template.html")
    

    select_file_template.html

    <html>
        <form method="POST" enctype="multipart/form-data">
            {% csrf_token %}
            <input type="file" name="file_name" onchange="form.submit()"/>
        </form>
    </html>
    
    Login or Signup to reply.
  3. This is more of a shared scratchpad than an answer. I am going to delete it later.

    From a previous discussion in comments: the files are dynamically generated (i.e., the content is different every time), but take no parameters.

    Changes explained:

    • before, a file was generated when the request type is POST and a file name is provided. This creates uncertainty for POST without a filename or filename with a GET request. Getting rid of the POST portion of it, filename is to be passed as a GET parameter. Also, semantics of HTTP request types is that POST is changing something on server
    • requested file is validated to be among available filenames. This substantially reduces the surface of attack for potential exploits
    • Since it’s a GET request, no form is needed. Simple <a href=..> links will do.
    IP_FILES_PATH = "mysite/ip_print_files/"
    
    def print_ip(request):
        filename = request.GET.get('filename')
    
        if not filename:
            return render(request, "print_ip.html", {"files": ip_files_list})
    
        # IMPORTANT: validate the input
        ip_files_list = os.listdir(IP_FILES_PATH)
        fpath = os.path.join(IP_FILES_PATH, filename)
        if filename not in ip_files_list or not os.path.isfile(fpath):
            raise Http404
    
        return HttpResponse(
            open(fpath, 'rb'), 
            content_type='text/plain',  # it's a download, mime type doesn't matter
            headers={
                'Content-Disposition': f"attachment; filename={filename}",
                'Cache-Control': 'no-cache'  # files are dynamic, prevent caching
            }
        )
    

    The template:

    ## ...
    {% block main %}
        <h1>Print Ip</h1>
        {% for file in files %}
            <p><a href="?filename={{ file }}" target="_blank"></a></p>
        {% endfor %}
    {% endblock %}
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search