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

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))

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

Flask: How to upload a user file into Google Cloud Storage using upload_blob

I'm using Flask to make a web application and I want to upload a user input file to Google Storage Cloud. I'm using Heroku to host my web app and I don't know how to save files on Heroku's temporary storage so I'm trying to use tempfile to store the file in a directory and then access the directory to upload the file.
When I try to do that, I get this error: PermissionError: [Errno 13] Permission denied: 'C:\\Users\\[MyName]\\AppData\\Local\\Temp\\tmpbpom7ull'
Here is my code I'm working with, if anyone has any other way to upload a FileStorage object to the Google Storage cloud or a way to access the saved file, that would be very appreciated!
# File is currently a "FileStorage" object from werkzeug, gotten by doing
# file = request.files["filename"]
tempdir = tempfile.mkdtemp()
file.name = filename
file.save(tempdir)
upload_blob(BUCKET_NAME,filename,filename)
Following up on yesterday's Flask: Could not authenticate question the Google Cloud Storage client, you can use werkzeug's FileStorage object as described in the Flask-GoogleStorage usage:
Assuming you a have a file hellofreddie.txt in the working directory:
hellofreddie.txt:
Hello Freddie!
You can then open it, create a FileStorage object and then use the save on Bucket object (files):
from datetime import timedelta
from flask import Flask
from flask_googlestorage import GoogleStorage, Bucket
from werkzeug.datastructures import FileStorage
import os
files = Bucket("files")
storage = GoogleStorage(files)
app = Flask(__name__)
app.config.update(
GOOGLE_STORAGE_LOCAL_DEST = app.instance_path,
GOOGLE_STORAGE_SIGNATURE = {"expiration": timedelta(minutes=5)},
GOOGLE_STORAGE_FILES_BUCKET = os.getenv("BUCKET")
)
storage.init_app(app)
with app.app_context():
with open("hellofreddie.txt","rb") as f:
file = FileStorage(f)
filename = files.save(file)
After the code has run, you will see a UUID-named equivalent created in Cloud Storage.
You can use the storage browser or gsutil:
gsutil ls gs://${BUCKET}
gs://{BUCKET}/361ea9ea-5599-4ff2-84d1-3fe1a802ac08.txt
NOTE I was unable to resolve an issue trying to print either files.url(filename) or files.signed_url(filename). These methods correctly return the Cloud Storage Object but as PurePosixPath('f3745268-5c95-4c61-a892-09c0de556635.txt'). My Python naivete.
I've realized my error, I was trying to use file.save() to a folder and not to an actual file, my code has been updated to
tempdir = tempfile.mkdtemp()
file.name = filename
file.save(tempdir + "/" + filename)
upload_blob(BUCKET_NAME,tempdir + "/" + filename,filename)
Thank you to PermissionError: [Errno 13] Permission denied

deny already uploaded files in flask

Hi I am uploading files with flask.
Somehow something recently broke and we want to deny already existing files. Which it used to do automatically. Now while adjusting the function I don't see it working somehow. I tried a couple things and I am stuck on this now. We do a curl request from another server and the upload is automated. We have a frontend on there but purely cause of the current state of the application what is relevant is that we list a directory and we want to forbid overwritting those files.
DENY_UPLOADED_IMAGES = os.listdir(f)
#app.route("/upload")
def upload():
if request.method == 'POST':
# check if the post request has the file part
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
# if user does not select file, browser also
# submit an empty part without filename
if 'file' in DENY_UPLOADED_IMAGES:
flash('File already uploaded')
return redirect(request.url)
if file.filename == '':
flash('No selected file')
return redirect(request.url)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return redirect(url_for('uploaded_file',
filename=filename))
return render_template('upload.html')
DENY_UPLOADED_IMAGES = os.listdir(f)
This, which is at the global level, only reads the list of currently uploaded files when your application is launched. Filenames won't be added to this list if they were uploaded since the app (or worker) restarted.
Probably better to check each one at the time of upload:
if os.path.exists(file.filename):
flash('File already uploaded')
return redirect(request.url)
This may still be open to a race condition if run with multiple WSGI workers.

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

Flask's send_file() function keeps changing directories while looking for the file [duplicate]

This question already has answers here:
Refering to a directory in a Flask app doesn't work unless the path is absolute
(1 answer)
Saving upload in Flask only saves to project root
(1 answer)
Get root path of Flask application
(2 answers)
Closed 4 years ago.
There are 2 functions or views located in the same file version.py, one for upload the other for downloading that file. The issue is that the send_file function keeps changing directories while opening the file.
Code 1:
File Upload function:
binfile = form.binfile.data
filename = secure_filename("arduino.bin")
#without the app folder
binfile.save(os.path.join(
app.config['UPLOAD_FOLDER'],
device_names.farm_id,
device_names.mqtt_id,
filename))
flash('File Uploaded', 'success')
File Download Function:
file_path = os.path.join(
app.config['UPLOAD_FOLDER'],
farm_id,
device_id,
'arduino.bin')
print(file_path)
response = make_response(send_file(
file_path,
mimetype='application/octet-stream',
as_attachment=True
))
response.headers['x-MD5'] = md5(file_path)
print(response.headers)
return response, 200
Here the file gets uploaded to the project root folder.
When I send a request for this file, I get this error:
[Errno 2] No such file or directory:
‘/home/maxwell/Desktop/python/aquaponics-monitor/app/firmware-manager/FARM0/node2/arduino.bin’
When I manually move the file to the 'app' folder I get no errors and a 200 response code while requesting. So I assume that the function is looking for the file inside the 'app' folder. I changed the upload location by adding 'app' to the path while uploading.
Code 2:
File Upload Function:
binfile = form.binfile.data
filename = secure_filename("arduino.bin")
#app folder included
binfile.save(os.path.join(
'app',
app.config['UPLOAD_FOLDER'],
device_names.farm_id,
device_names.mqtt_id,
filename))
flash('File Uploaded', 'success')
The file download function is the same as code 1. For this code I get this error:
[Errno 2] No such file or directory:
‘firmware-manager/FARM0/node2/arduino.bin’
The file download function now looks in the project root folder. I manually moved the file to the root folder this time and I again get a status code 200 and no errors when I send a request for it.
The only code change I made in the entire application is adding 'app' to the path in the File Upload function. Why is this happening?

Categories

Resources