0

I have a JSON file in the flask app that i send via either send_from_directory() or via send_file() i have tried both. This triggers on button click but the browser opens the file directly instead of getting a download dialog box. I researched a bit and found out Firefox and Chrome have an inbuilt option to do this, however i want it to always download instead of opening no matter the setting in the browser.

Flask Code

def download_file():
    filename = "requiredFields.json"

    return send_from_directory(app.config['MAIN_FOLDER'],filename,as_attachment=True)

HTML That calls it

<a href="{{url_for('download_file',filename = file)}}"class="btn btn-primary" type="button">Download</a>
DarkArcane
  • 55
  • 5
  • probably if you send it with `content-type`/`mime-type` like for `zip` or `exe` file then browser as default should download it. – furas Aug 06 '20 at 15:51
  • [Do I need Content-Type: application/octet-stream for file download?](https://stackoverflow.com/questions/20508788/do-i-need-content-type-application-octet-stream-for-file-download) – furas Aug 06 '20 at 15:54

1 Answers1

0

If you set mimetype to 'application/octet-stream' then browser should save it.

send_file(..., mimetype='application/octet-stream')

send_from_directory(..., mimetype='application/octet-stream')

Doc: send_file

See also: Do I need Content-Type: application/octet-stream for file download?


EDIT:

Minimal working example - you have to only set correct filenames in index()

It doesn't matter if you have file .json, .txt, image, .pdf or other. It works for all types of files.

from flask import Flask, render_template_string, send_from_directory

app = Flask(__name__)
app.config['MAIN_FOLDER'] = '.'

@app.route('/')
def index():
    all_filenames = [
        'requiredFields.json',
        'input.txt',
        'image.jpg',
        'document.pdf',
    ]
    
    return render_template_string('''
{% for file in all_files %}
    <a href="{{ url_for('download_file', filename=file) }}">Download {{file}}</a><br/>
{% endfor %}''', all_files=all_filenames)

#@app.route('/download_file/<filename>')
@app.route('/download_file/<path:filename>')
def download_file(filename):
    return send_from_directory(app.config['MAIN_FOLDER'], filename, as_attachment=True, attachment_filename=filename, mimetype='application/octet-stream')
    
if __name__ == '__main__':    
    app.run() #debug=True 
furas
  • 134,197
  • 12
  • 106
  • 148
  • Hey Thanks for the response, but that still didn't work:(. Now i have two routes doing the same type of thing one is serving a JSON and one is serving a txt. Now i originally asked the question for the JSON one but that started working right as soon as i removed the filename attribute from the tag, now I'm having a problem with the txt one(Code is same) where i cannot remove that attribute and mimetype isn't fixing it:(. – DarkArcane Aug 06 '20 at 19:31
  • for flask and browser there is no difference if you send JSON or txt - both are only bytes - and in both you can use the same code with the same `mimetype='application/octet-stream'` to force browser to download it. As for parameter `filename = file` in `url_for()` - it needs `filename` in route `@app.route('/some_page/')` and in function `def download_file(filename):` – furas Aug 06 '20 at 21:04
  • So your answer was actually perfect, i do have all of those parameters in the code just didn't include them here. Interestingly running the code the second time worked like a charm for some reason, didn't change a thing xD – DarkArcane Aug 07 '20 at 09:01