i created a little web app that allows to upload and download files (pdf,txt,docx) to a sqlite3 database. I use flask as backend framework. When i hit the download button the download route gets called and everything seems fine but the download tab of the browser is not opening and nothing gets downloaed. The print statement logs a valid filename to the terminal. Where did i go wrong ?
html snippet for upload and download :
{% block main %}
<div class="grid-item">
<h4>Datenbank</h4>
<table style="display: flex;justify-content: space-evenly;">
<tr >
<td>
download
</td>
<td>
Datei
</td>
<td>
löschen
</td>
</tr>
{% for file in current_user.files %}
<tr>
<td>
<button type="button" class="close" onClick="download_files({{ file.id }})">
<i class="bi bi-box-arrow-down" style="color: black;"></i>
</button>
</td>
<td>
<li class="list-group-item">
{{ file.filename }}
</li>
</td>
<td >
<button type="button" class="close" onClick="delete_files({{ file.id }})" >
<span aria-hidden="true" style="color:black">×</span>
</button>
</td>
</tr>
{% endfor %}
</table>
</div>
{% endblock %}
{% block scripts %}
<script type="text/javascript" src="{{ url_for('static', filename='index.js') }}"></script>
{% endblock%}
javascript used:
function download_files(fileid) {
fetch("/download", {
method: "POST",
body: JSON.stringify({ fileid: fileid }),
})
}
flask code:
@files.route("/download" , methods=["POST"])
@login_required
def download():
file = json.loads(request.data)
fileID = file["fileid"]
file = Upload.query.filter_by(id=fileID).first()
if file:
if file.user_id == current_user.id:
print(f"file {file.filename} downloaded !" )
return send_file(BytesIO(file.data), download_name=file.filename,as_attachment=True )
EDITE 1 : Upload code html:
<div class="grid-item">
<h4>Datei hochladen</h4>
<h5>Unterstütze Dateiformate: .doc .docx .pdf .txt .odt</h5>
<form action="{{ url_for('files.upload') }}" id="dropzone" class="dropzone" multiple accept=".doc,.docx,.pdf,.txt,.odt">
</form>
</div>
python:
@files.route("/upload", methods=["GET", "POST"])
@login_required
def upload():
if request.method == 'POST':
allowed_files = [".doc",".docx",".pdf",".txt",".odt"]
file = request.files['file']
file_ext = os.path.splitext(file.filename)[1]
if file_ext not in allowed_files:
flash(f"Dateiformat {file_ext} nicht unterstütt! ",category="error")
return render_template("drive.html")
elif not file.filename == '':
upload = Upload(filename=secure_filename(file.filename), data=file.read(),date=datetime.datetime.now(),user_id=current_user.id)
db.session.add(upload)
db.session.commit()
flash(f"Dateien hochgeladen ! ",category="sucess")
return redirect(url_for("views.drive"))
else:
flash(f"Ungültiger Dateiname Dateiname: {file.filename} ! ",category="error")
return render_template("drive.html")
else:
return render_template("drive.html")
I did check that the correct file gets submited to the download function. Also that the file exists and that the funcion runs till the return statement. I checked the flask send_file documentation for mistakes of the send_file() function and checked the dev tools of chrome to see if there is a payload.
EDIT 2: I did try it with the following code which works. Maybe someone can explain the difference to me. From my consol logs my code does exactly the same, just gets the id through javascript fetch() and not by a flask route credential.
@app.route('/download/<upload_id>')
def download(upload_id):
upload = Upload.query.filter_by(id=upload_id).first()
return send_file(BytesIO(upload.data), download_name=upload.filename, as_attachment=True)
The reason why i dont use this is, because i dont know how to send the file id to the flask route without a javascript part. Maybe someone can help me on that part.
Not a real answer to the problem but a working solution:
I changed the download route to :
@files.route('/download/<file_id>', methods=["POST"])
@login_required
def download(file_id):
if request.method == "POST":
file = Uploads.query.filter_by(id=file_id).first()
return send_file(BytesIO(file.data), download_name=file.filename, as_attachment=True)
The file gets selected by the file_id send by the html file
<form action="/download/{{file.id}}" method="post" >
<button type="submit" class="close" >
<i class="bi bi-download" style="color:black;width: 2%;height: 2%"></i>
</button>
</form>