Flask send_file not sending file
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)
Comments
-
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 over 3 yearsThat would be much nicer, if it works! But doesn't
send_file()
run asynchronously ? How can you be sure thatos.remove()
runs aftersend_file()
has completed here? -
Rob Bricheno about 3 yearsThis 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 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 about 3 yearsIf 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.