Flask send_file not sending file

21,167

Solution 1

You need to return the result of send_file:

@app.route('/', methods=["GET", "POST"])
def index():
    if request.method == "POST":
        link = request.form.get('Link')
        with youtube_dl.YoutubeDL(ydl_opts) as ydl:
            info_dict = ydl.extract_info(link, download=False)
            video_url = info_dict.get("url", None)
            video_id = info_dict.get("id", None)
            video_title = info_dict.get('title', None)
            ydl.download([link])
        print("sending file...")
        return send_file("dl/"+video_title+".f137.mp4", as_attachment=True)
    else:
        return render_template("index.html", message=message)

Unfortunately, this will make it harder for you to "clean up" after sending the file, so you probably want to do that as part of scheduled maintenance (e.g. run a cron job to delete old downloaded files). See here for more information about the problem.

Solution 2

As Rob Bricheno said,

You need to return the result of send_file

So you can save the result of "flask.send_file", then clean up, then return the result.

print("sending file...")
result = send_file("dl/"+video_title+".f137.mp4", as_attachment=True)
print("file sent, deleting...")
os.remove("dl/"+video_title+".f137.mp4")
return result

Solution 3

Extending @Rob Bricheno's answer, if you need to clean up after the request, you can create a deferred method that will execute after the request is complete:

@app.route('/', methods=["GET", "POST"])
def index():
    if request.method == "POST":
        link = request.form.get('Link')
        with youtube_dl.YoutubeDL(ydl_opts) as ydl:
            info_dict = ydl.extract_info(link, download=False)
            video_url = info_dict.get("url", None)
            video_id = info_dict.get("id", None)
            video_title = info_dict.get('title', None)
            ydl.download([link])
        print("sending file...")
        response = send_file("dl/"+video_title+".f137.mp4", as_attachment=True)
        # Create handle for processing display results (non-blocking, excecutes after response is returned)
        @flask.after_this_request
        def add_close_action(response):
            @response.call_on_close
            def process_after_request():
                try:
                    print("file sent, deleting...")
                    os.remove("dl/"+video_title+".f137.mp4")
                    print("done.")
                except Exception as e:
                    logger.exception(str(e))
        return response
    else:
        return render_template("index.html", message=message)
Share:
21,167
jackmerrill
Author by

jackmerrill

I'm a fullstack developer and graphic designer!

Updated on May 01, 2021

Comments

  • jackmerrill
    jackmerrill about 3 years

    I'm using Flask with send_file() to have people download a file off the server.

    My current code is as follows:

    @app.route('/', methods=["GET", "POST"])
    def index():
        if request.method == "POST":
            link = request.form.get('Link')
            with youtube_dl.YoutubeDL(ydl_opts) as ydl:
                info_dict = ydl.extract_info(link, download=False)
                video_url = info_dict.get("url", None)
                video_id = info_dict.get("id", None)
                video_title = info_dict.get('title', None)
                ydl.download([link])
            print("sending file...")
            send_file("dl/"+video_title+".f137.mp4", as_attachment=True)
            print("file sent, deleting...")
            os.remove("dl/"+video_title+".f137.mp4")
            print("done.")
            return render_template("index.html", message="Success!")
        else:
            return render_template("index.html", message=message)
    

    The only reason I have .f137.mp4 added is because I am using AWS C9 to be my online IDE and I can't install FFMPEG to combine the audio and video on Amazon Linux. However, that is not the issue. The issue is that it is not sending the download request.

    Here is the console output:

    127.0.0.1 - - [12/Dec/2018 16:17:41] "POST / HTTP/1.1" 200 -
    [youtube] 2AYgi2wsdkE: Downloading webpage
    [youtube] 2AYgi2wsdkE: Downloading video info webpage
    [youtube] 2AYgi2wsdkE: Downloading webpage
    [youtube] 2AYgi2wsdkE: Downloading video info webpage
    WARNING: You have requested multiple formats but ffmpeg or avconv are not installed. The formats won't be merged.
    [download] Destination: dl/Meme Awards v244.f137.mp4
    [download] 100% of 73.82MiB in 00:02
    [download] Destination: dl/Meme Awards v244.f140.m4a
    [download] 100% of 11.63MiB in 00:00
    sending file...
    file sent, deleting...
    done.
    127.0.0.1 - - [12/Dec/2018 16:18:03] "POST / HTTP/1.1" 200 -
    

    Any and all help is appreciated. Thanks!

  • Rob Bricheno
    Rob Bricheno over 3 years
    That would be much nicer, if it works! But doesn't send_file() run asynchronously ? How can you be sure that os.remove() runs after send_file() has completed here?
  • Rob Bricheno
    Rob Bricheno about 3 years
    This is cool! But apparently it will only work on Linux (which lets the file be read even after deletion if there is still an open file pointer to it). I found some more interesting discusion of this and other techniques here stackoverflow.com/questions/24612366/…
  • VoteCoffee
    VoteCoffee about 3 years
    @Rob Bricheno Mine is different than the link. It might fix his issue as well. after_this_request runs after the request but before the response is sent (last opportunity to modify the response essentially). The extra response.call_on_close defers it until after the response has been sent. So it should work on linux and windows.
  • VoteCoffee
    VoteCoffee about 3 years
    If it's an issue of the file needing to exist even after the response has been sent, you'd need to do a temp file workaround. But my guess is that send_file is part of the response itself.