1

I need to let the Django auto download the generated file.

Tried all different solutions online, none of them works.

Views.py

def validate(request):
    if request.method == 'POST':
        filename = request.POST.get('source_file')
        file_path = os.path.join(settings.MEDIA_ROOT, 'SourceFiles', filename)
        region = request.POST.get('region')
        product_type = request.POST.get('product_type')
        result = validateSource.delay(file_path, region, product_type)
        output_filepath, log_filepath = result.get()
        if os.path.exists(output_filepath) and os.path.exists(log_filepath):
            zip_filename = zipFiles([output_filepath, log_filepath], filename)
            zip_filepath = os.path.join(settings.MEDIA_ROOT, zip_filename)
            response = FileResponse(open(zip_filepath, 'rb'), as_attachment=True)
            return response
        raise Http404

Template: code for the form POST.

     $(document).on('submit', '#productForm', function(e){
       e.preventDefault();
       var inputFilePath = document.getElementById('sourceFileInput').files.item(0).name;
        $.ajax({
            method: 'POST',
            url: 'validate/',
            data: {
              source_file: inputFilePath,
              region: $("#Region-choice").val(),
              product_type: $("#Product-type").val()}
            })
            .done(function(){
                document.getElementById('lblStatus').innerHTML = "Result: <br/>"
                document.getElementById('lblStatusContent').innerHTML = "Success!"

            })
            .fail(function(req, textStatus, errorThrown) {
                document.getElementById('lblStatus').innerHTML = "Result: <br/>"
                alert("Something went wrong!:" + textStatus + '  ' + errorThrown )
            });
    });
 });
flgn
  • 789
  • 6
  • 12
  • "not working" is not very descriptive. What happens? – dirkgroten May 22 '19 at 14:35
  • @dirkgroten well, nothing happends when the response returned. I am expecting the file to be automatically downloaded when the FileResponse returned. – flgn May 22 '19 at 14:37
  • but if the `FileResponse` is returned, you would either see the file inline in your browser or it would be downloaded. What do you see in your browser Network tab? what are the headers of response? Do you see the file being returned there? And is the Content-Disposition header set for the response? – dirkgroten May 22 '19 at 14:39
  • Could you be making a GET request? Is it returning a 404 because the file doesn't exist? Did you try doing a simple view that returns the FileResponse and then working backwards adding in more of your logic? – schillingt May 22 '19 at 14:40
  • @dirkgroten yes, I can see the file in the response headers. And for the `Content-Dispositioin`, according to the offcial docs, **If as_attachment=True, the Content-Disposition header is set, which asks the browser to offer the file to the user as a download.**, seems I don't need to set it manually anymore. And in the response, it says **Content-Disposition: attachment; filename="Final.zip"**, looks correct. – flgn May 22 '19 at 14:43
  • what makes the POST request? If is a normal html form submission or is it an xhr? – dirkgroten May 22 '19 at 14:46
  • @dirkgroten yes, from a normal html form. – flgn May 22 '19 at 14:47
  • @schillingt The file does exist and it is also in the response headers. – flgn May 22 '19 at 14:49
  • have you tried different browsers? I'm wondering if responding to a POST with a file to download is always treated the same way. Note that in general, you should *always* redirect on a POST so you then have a GET to fetch the success page. That's because otherwise you "break" the back button. – dirkgroten May 22 '19 at 15:30
  • and have you tried with something else than a zip file? I think the browser could be a cause here. – dirkgroten May 22 '19 at 15:31
  • @dirkgroten Yes, also tried in FF, still the same. For redirecting, I doubt if I should use that, because I want to stay on the same page and without refreshing the page. – flgn May 22 '19 at 15:33
  • @dirkgroten Tried with a simple and small .txt file, no magic happened... – flgn May 22 '19 at 15:38
  • Might also be better to put the template code here... – flgn May 22 '19 at 15:45
  • you said is was a normal html form post, but apparently it's not: you're making an XHR request! – dirkgroten May 22 '19 at 15:52
  • @dirkgroten okay, then I mixed them up... Could this be the issue then? – flgn May 22 '19 at 15:55
  • yes of course, you're not doing anything with the file that's returned in your `done` handler. – dirkgroten May 22 '19 at 15:56
  • Possible duplicate of [Download a file by jQuery.Ajax](https://stackoverflow.com/questions/4545311/download-a-file-by-jquery-ajax) – dirkgroten May 22 '19 at 15:59

2 Answers2

2

It's not possible to download files to your computer via an ajax (XHR) request. So you need to redirect the user actually (setting window.location) to a view that downloads the file. Or you can add as a result of the successful POST a button the current page so the user can download the file. In any case, you need to move the file download to a different view so a standard GET request can fetch it.

But your code to return the file in Django (using FileResponse) is correct.

There's also an explanation with an alternative way of doing it here

dirkgroten
  • 20,112
  • 2
  • 29
  • 42
0
def validate(request):
    if request.method == 'POST':
        filename = request.POST.get('source_file')
        file_path = os.path.join(settings.MEDIA_ROOT, 'SourceFiles', filename)
        region = request.POST.get('region')
        product_type = request.POST.get('product_type')
        result = validateSource.delay(file_path, region, product_type)
        output_filepath, log_filepath = result.get()
        if os.path.exists(output_filepath) and os.path.exists(log_filepath):
            zip_filename = zipFiles([output_filepath, log_filepath], filename)
            zip_filepath = os.path.join(settings.MEDIA_ROOT, zip_filename)
            with open(zip_filepath, 'rb') as fh:
                response = HttpResponse(fh.read(), content_type="application/force-download")
                response['Content-Disposition'] = 'attachment; filename=' + os.path.basename(zip_filepath)
                return response
        raise Http404
Amine Messaoudi
  • 2,141
  • 2
  • 20
  • 37