tar files and download with flask app - python

I have one tex file and three images and I want that the user can click a button and download all three of them. It would be ideal if the four files would be as one tar file. My download works as follows right now
#app.route('/download_tex', methods=['GET', 'POST'])
#login_required
def download_tex():
latext_text = render_template('get_lates.html')
filename = 'test'
response = make_response(latext_text)
response.headers["Content-Disposition"] = "attachment; filename=%s.tex" % filename
return response
this works fine for the tex file, but how can I tar up files within the flask app and send the tar file instead?
EDIT: Ok thanks to the comment below I came up with this code
latext_text = render_template('get_latex.html')
latex_file = open(basedir + '/app/static/statistics/latex_%s.tex' % current_user.username, "w")
latex_file.write(latext_text)
latex_file.close()
filename = 'tarfile_%s.tar.gz' % current_user.username
filepath = basedir + '/app/static/statistics/%s' % filename
tar = tarfile.open(filepath, "w:gz")
tar.add(basedir + '/app/static/statistics/image1.png')
tar.add(basedir + '/app/static/statistics/image2.png')
tar.add(basedir + '/app/static/statistics/image3.png')
tar.add(basedir + '/app/static/statistics/latex_%s.tex' % current_user.username)
tar.close()
but how can I now download that tar file with the browser?

You should use the send_from_directory method that Flask provides for this =) It is the perfect use case for what you're doing.
What you can do is something like this:
from flask import send_from_directory
# code here ...
filename = 'tarfile_%s.tar.gz' % current_user.username
filedir = basedir + '/app/static/statistics/'
# tar code here ...
return send_from_directory(filedir, filename, as_attachment=True)
That will handle all of the downloading bits for you in a clean way.

Related

How to copy a file box URL rooted in folder directory?

I need to create an script that helps me pick the URL from a Box file that has a path in my computer.
I've the Box desktop application installed.
Like: C:\Users\Thiago.Oliveira\Box\
I've created this script:
# Providing the folder path
origin = r'C:\\Users\\Thiago.Oliveira\\Box\\XXXXXXX.xlsx'
target = f'C:\\Users\\Thiago.Oliveira\\Box\\XXXXXXX{datetime.date.today()}.xlsx'
# Fetching the list of all the files
shutil.copy(origin, target)
print("Files are copied successfully")
This helps me out to copy and rename the file for this box folder. But I also want to pick up the URL from the newly created file so I can send it over in an e-mail.
I haven't found anything that would help me with this.
Is this possible?
Yes, you can, see the example below.
This example is using the box python sdk, and this is a JWT auth application script.
After uploading the file, the box sdk returns a file object, which has many properties.
I'm not sure what you mean by "pick up the URL", it could be the direct download URL of the file or a shared link.
The example is for the direct download URL.
To get the destination FOLDER_ID, you can look at the browser URL when you open the folder on the box.com app.
e.g.:image of demo folder url in browser
from datetime import date
import os
from boxsdk import JWTAuth, Client
def main():
auth = JWTAuth.from_settings_file(".jwt.config.json")
auth.authenticate_instance()
client = Client(auth)
folder_id = "163422716106"
user = client.user().get()
print(f"User: {user.id}:{user.name}")
folder = client.folder(folder_id).get()
print(f"Folder: {folder.id}:{folder.name}")
with open("./main.py", "rb") as file:
basename = os.path.basename(file.name)
file_name = os.path.splitext(basename)[0]
file_ext = os.path.splitext(basename)[1]
file_time = date.today().strftime("%Y%m%d")
box_file_name = file_name + "_" + file_time + file_ext
print(f"Uploading {box_file_name} to {folder.name}")
box_file = folder.upload_stream(file, box_file_name)
box_file.get()
print(f"File: {box_file.id}:{box_file.name}")
print(f"Download URL: {box_file.get_download_url()}")
if __name__ == "__main__":
main()
print("Done")
Resulting in:
User: 20344589936:UI-Elements-Sample
Folder: 163422716106:Box UI Elements Demo
Uploading main_20230203.py to Box UI Elements Demo
File: 1130939599686:main_20230203.py
Download URL: https://public.boxcloud.com/d/1/b1!5CgJ...fLc./download

How to upload a file to your website hosted by Heroku?

I developed a website that would take a user provided file and extract relevant data out of it, I used Python/Flask for the backend and the website is stored in Heroku.
The application is working fine on my local machine, but when I run it in Heroku, whenever I attempt to upload/process a file I get this message on Heroku's logs:
2022-02-14T17:27:48.000421+00:00 app[web.1]: FileNotFoundError: [Errno 2] No such file or directory: '/application/uploads/report.html'
My python code that goes around the file uploading is:
app.config['UPLOAD_PATH'] = './application/uploads'
#app.route('/read', methods=['GET', 'POST'])
def read():
form = ReadForm()
if form.validate_on_submit():
if request.method == 'POST':
files = request.files.getlist('read')
for file in files:
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_PATH'], filename))
return redirect(url_for('show'))
return render_template('read.html', form=form)
How do I make the uploaded files available to my application? After reading the file, the application just delete it.
I saw a question with a very similar title as mine, here:
How to upload file on website hosted by Heroku?
And one of the answers there, suggests using Amazon's S3, is it the only solution?
#buran pointed me to another post here which helped me solving the issue: Python Flask upload file to app folder on server (heroku)
I added the following to my path definition:
base_path = os.path.dirname(__file__)
So the final one would be:
# Path for the files upload part
app.config['UPLOAD_PATH'] = '/uploads'
base_path = os.path.dirname(__file__)
Then when uploading a file I refer to the path like this:
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(base_path + "/" + app.config['UPLOAD_PATH'], filename))
So, instead of:
file.save(os.path.join(app.config['UPLOAD_PATH'], filename))
I used:
file.save(os.path.join(base_path + "/" + app.config['UPLOAD_PATH'], filename))

Can i have two different sets of app configurations?

I have made a flask server REST API that does an upload to a specific folder.
The upload must comply to a specific file extension, and must be in a specific folder.
So i made these two app.configs:
app.config['UPLOAD_EXTENSIONS'] = ['.stp', '.step']
app.config['UPLOAD_PATH'] = 'uploads'
However, i want to make a new route, for a new upload of a specific file extension to another folder.
So can i have two sets of app.config['UPLOAD_EXTENSIONS'] and app.config['UPLOAD_PATH']?
One set will be for extension1 in folder1 and the other set for extension2 in folder2.
Try using the extension Flask-Uploads .
Or, proceeding from the file format, form a subdirectory in your UPLOAD_PATH.
import os
def select_directory(filename: str) -> str:
file_format = filename.split('.')[1]
your_path1 = 'path1'
your_path2 = 'path2'
if file_format in ('your format1', 'your format2'):
full_path = os.path.join(app.config['UPLOAD_FOLDER'], your_path1, filename)
else:
full_path = os.path.join(app.config['UPLOAD_FOLDER'], your_path2, filename)
return full_path

Streamlit, Flask, Fastapi: How to upload zipped files to my web app and make them downloadable?

I am working on an app using streamlit to upload a zipped file and enable users to download that zipped file. For a reference example, see how online services take in a word file, convert it into pdf and make that pdf downloadable automatically.
I have been able to complete the first 2 steps. Requesting assistance with uploading the file and making it downloadable. Thank you.
This is how it is done.
import streamlit as st
# zipping the different parts of the song
def zipdir(dst, src):
zf = zipfile.ZipFile(dst, "w", zipfile.ZIP_DEFLATED)
abs_src = os.path.abspath(src)
for dirname, subdirs, files in os.walk(src):
for filename in files:
absname = os.path.abspath(os.path.join(dirname, filename))
arcname = absname[len(abs_src) + 1:]
print('zipping %s as %s' % (os.path.join(dirname, filename),
arcname))
zf.write(absname, arcname)
zf.close()
zip_path = 'Python.zip'
zipdir(zip_path, path)
# send to webapp to make downloadable link
with open(zip_path, "rb") as f:
bytes_read = f.read()
b64 = base64.b64encode(bytes_read).decode()
href = f'<a href="data:file/zip;base64,{b64}" download=\'{title}.zip\'>\
Click to download\
</a>'
st.sidebar.markdown(href, unsafe_allow_html=True)

Delete temporary folder after returning using send_file() of flask python on Windows Server

I am trying to delete a temporary folder where I save an image and do some processing on it and then return some results. I used shutil.rmtree(filepath) and it works correctly on Mac OS. However when I run it on Windows server, it fails with an error saying file is still in use.
I referred this question on stack overflow How to clean up temporary file used with send_file?
This answer using weak references helps in removing the error that was thrown on Windows, but the folder is still not deleted. Though it still works correctly on Mac OS.
How can I delete the temporary folder after the request has been completed on Windows system?
Here is my code snippet for your reference.
def post(self):
try:
if 'photo' not in request.files:
raise NoFilesError
file = request.files['photo']
print('file = ', file)
if file.filename == '':
raise NoFilesError
if file and self._allowed_file(file.filename):
filename = secure_filename(file.filename)
dir_path = os.path.join(BaseAPIConfig.UPLOAD_FOLDER, get_timestamp())
create_directory(dir_path)
file_path = os.path.join(dir_path, filename)
file.save(file_path)
img_proc_main(dir_path, filename)
image_filename = filename.rsplit('.', 1)[0]
image_format = filename.rsplit('.', 1)[1]
result_file = os.path.join(dir_path, image_filename + '_bb.' + image_format)
response = make_response(send_file(result_file))
file_remover = FileRemover()
file_remover.cleanup_once_done(response, dir_path)
return response
...
FileRemover class
class FileRemover(object):
def __init__(self):
self.weak_references = dict() # weak_ref -> filepath to remove
def cleanup_once_done(self, response, filepath):
wr = weakref.ref(response, self._do_cleanup)
self.weak_references[wr] = filepath
def _do_cleanup(self, wr):
filepath = self.weak_references[wr]
print('Deleting %s' % filepath)
shutil.rmtree(filepath, ignore_errors=True)
You are using ignore_errors=True, which hides away any exception that shutil.rmtree encounters.
There are many reasons why shutil.rmtree might fail to remove the directory—perhaps you simply don't have the correct permission on Windows. Setting ignore_errors to false will give you more indications of what's going on.

Categories

Resources