Downloading multiple files in Flask - python

I am trying to provide the client side the option of downloading some files in Flask. There can be multiple files or a single file available for the user/client to download.
However I am not able to understand how to provide the user the option to download multiple files.
Here is what I have tried so far:
#app.route('/download_files')
def download():
count=0
download_list=[]
for path in pathlib.Path("dir1/dir2").iterdir():
if path.is_file():
for i in names:
if pathlib.PurePosixPath(path).stem == i:
count += 1
download_list.append(path)
return send_file(download_list, as_attachment=True, mimetype="text/plain", download_name="Downloaded Files", attachment_filename="Generated Files")
This does not work properly even with a single file. The file type I am trying to download is text file with the extension .sql .
Will I somehow have to zip multiple files and then provide the download option? Please guide with my available options.

In order to offer several files together as a download, you only have the option of compressing them in an archive.
In my example, all files that match the specified pattern are listed and compressed in a zip archive. This is written to the memory and sent by the server.
from flask import Flask
from flask import send_file
from glob import glob
from io import BytesIO
from zipfile import ZipFile
import os
app = Flask(__name__)
#app.route('/download')
def download():
target = 'dir1/dir2'
stream = BytesIO()
with ZipFile(stream, 'w') as zf:
for file in glob(os.path.join(target, '*.sql')):
zf.write(file, os.path.basename(file))
stream.seek(0)
return send_file(
stream,
as_attachment=True,
download_name='archive.zip'
)

You haven't provided a code sample where you actually getting these files or this file. Minimum working example would be like this:
from flask import Flask, request
app = Flask(__name__)
#app.route('/download_files', methods=['POST'])
def download():
file = request.files['file'] # for one file
files = request.files.getlist("file[]") # if there're multiple files provided
if __name__ == "__main__":
app.run()
After what your file variable will be an object of werkzeug.FileStorage and files variable will be a list of these objects.
And to download all these files you can check this question.

Related

Convert .doc/.docx to .pdf from URL, on-the-fly, with Python, on Linux

I need to capture .doc or .docx files from external sites, convert them to pdf and return the content. To this I add a content-type header, publish through my CMS, cache by CDN, and display within HTML using the Adobe PDF Embed API. I'm using Python 3.7.
As a test, this works:
def generate_pdf():
subprocess.call(['soffice', '--convert-to', 'pdf',
'https://arbitrary.othersite.com/anyfilename.docx'])
sleep(1)
myfile = open('anyfilename.pdf', 'rb')
content = myfile.read()
os.remove('anyfilename.pdf')
return content
This would be nice:
def generate_pdf(url):
result = subprocess.call(['soffice', '--convert-to', 'pdf', url])
content = result
return content
The URLs could include any parameters or illegal characters, which might make it hard to guess the resulting file name. Anyway, it would be preferable not to have to sleep, save, read, and delete the converted file.
Is this possible?
I don't think soffice supports outputting to stdout so you don't have many choices. If you output to a temporary directory, you can use listdir to get the filename though:
import subprocess
import tempfile
import os
url = "https://www.usariem.army.mil/assets/docs/journal/Lieberman_DS_survey_and_guidelines.docx"
with tempfile.TemporaryDirectory() as tmpdirname:
subprocess.run(["soffice", '--convert-to', 'pdf', "--outdir", tmpdirname, url], cwd="/")
files = os.listdir(tmpdirname)
if files:
print(files[0])

Import JSON Files from an entire directory into a MongoDB via a Python script

I want to import a directory with multiple sub directories and a lot of JSON-files into a MongoDB via a python script. However I can only import multiple JSON via GUI in Compass or one file at a time using a script using the following code I gathered from another question at stackoverflow(How to import JSON file to MongoDB using Python):
import json
from pymongo import MongoClient
client = MongoClient('localhost', 27017)
db=client['acme']
collection_posts = db ['posts']
with open('9995-f0763044.json') as f:
file_data = json.load(f)
collection_posts.insert_one(file_data)
client.close()
How can I change this so I can loop through an entire directory and import all of the JSON files? I have seen the insert_many() method but as far I understood it the specific filenames still have to be written into the code. In my perfect scenario I would just enter a directory in the script and it will scan and upload all the JSON-files in that directory. Is this even possible? Thanks for your help
something like this?
import glob
filelist = glob.glob('your/path/*.json')
for filename in filelist:
with open(filename) as f:
file_data = json.load(f)
collection_posts.insert_one(file_data)
client.close()

Download CSV File in Flask Best Practice

Right now I have a flask app in which part of the functionality allows me to select a date range and see data from a sql database from that selected date range. I then can click a button and it exports this to a csv file which is just saved in the flask project directory. I want the user to be able to download this csv file. I want to know what the best practice for a user to download a dynamic csv file. Should I send_file() and then delete the file after user has downloaded since this data shouldn't be saved and the user won't be using that file again. Should the file be saved to the database and then deleted out of the db? Or can I just keep it within the flask directory? Please provide insight if possible, thank you so much.
#brunns pointed it in very right direction.
You don't have to save the file in your database or in your file structure or anywhere. It will get created in memory on user request.
I've done this with django for pdf and for csv files it'll work in the same way with flask too. Basics are all same.
for python3 use io.StringIO, for python2 use StringIO
from io import StringIO
import csv
from flask import make_response
#app.route('/download')
def post(self):
si = StringIO.StringIO()
cw = csv.writer(si)
cw.writerows(csvList)
output = make_response(si.getvalue())
output.headers["Content-Disposition"] = "attachment; filename=export.csv"
output.headers["Content-type"] = "text/csv"
return output
Courtesy: vectorfrog
Based on #xxbinxx's answer, used with pandas
from io import StringIO
import csv
from flask import make_response
#app.route('/download')
def download_csv(self, df: pd.DataFrame):
si = StringIO()
cw = csv.writer(si)
cw.writerows(df.columns.tolist())
cw.writerows(df.values.tolist())
output = make_response(si.getvalue())
output.headers["Content-Disposition"] = "attachment; filename=export.csv"
output.headers["Content-type"] = "text/csv"
return output

Download file from root directory using flask

I am generating a xlsx file which I would like to download once it is created. The file is created using a module called 'xlsxwriter'. It saves the file in my root directory, however I cant figure out how to access it via flask, so that it starts a download.
This is how I create the file:
workbook = xlsxwriter.Workbook('images.xlsx')
worksheet = workbook.add_worksheet()
worksheet.write(..someData..)
It saves the file in my root directory.
Now I am trying to access it in order to download it via flask:
app = Flask(__name__, static_url_path='')
#app.route('/download')
def download():
# do some stuff
return Response(
app.send_static_file('images.xlsx'),
mimetype="xlsx",
headers={"Content-disposition":
"attachment; filename=images.xlsx"})
However, I get a 404 Error. Is using send_static_file the correct way to go here?
I found a solution using 'send_file' instead. Providing the path to my file like so:
from flask import send_file
return send_file(pathToMyFile, as_attachment=True)

excel download with Flask-RestPlus?

How to implement an API endpoint to download excel file using Flask-RestPlus?
Previously I had implemented similar function using Pyramid. However that method didn't work here.
Here is the old code snippet:
workBook = openpyxl.Workbook()
fileName = 'Report.xls'
response = Response(content_type='application/vnd.ms-excel',
content_disposition='attachment; filename=%s' % fileName)
workBook.save(response)
return response
Thanks for the help.
send_from_directory provides a secure way to quickly expose static files from an upload folder or something similar when using Flask-RestPlus
from flask import send_from_directory
import os
#api.route('/download')
class Download(Resource):
def get(self):
fileName = 'Report.xls'
return send_from_directory(os.getcwd(), fileName, as_attachment=True)
I have assumed file is in current working directory. The path to download file can be adjusted accordingly.

Categories