I am planning to deploy a program via waitress for development purposes. I decided to use Flask Session to be able to create a unique identity per user.
app = Flask(__name__)
app.secret_key = secrets.token_hex(16)
app.config['UPLOAD_FOLDER'] = 'uploads'
app.config['SESSION_TYPE'] = 'filesystem'
app.config['SESSION_FILE_DIR'] = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'session_files')
Session(app)
@app.route('/upload', methods=['POST'])
def upload():
files = request.files.getlist('files')
if files:
if 'user_folder' not in session:
session['user_folder'] = str(uuid.uuid4())
user_upload_folder = os.path.join(app.config['UPLOAD_FOLDER'], session['user_folder'])
os.makedirs(user_upload_folder, exist_ok=True)
else:
user_upload_folder = os.path.join(app.config['UPLOAD_FOLDER'], session['user_folder'])
The problem with the code is that when I went and ran waitress and tested the code, I saw in the designated folder that the multiple files uploaded were separated into different folders when I am only expecting one unique folder per each instance. Does it help that I also checked the root of [‘SESSION_FILE_DIR’] and I noticed there were two different files created. Any solutions would be great
EDIT
Code I used to run the flask program
if __name__ == '__main__':
#serve(app, host='127.0.0.1', port=8080)
app.run(debug=True, use_reloader=False)
I used waitress serve() to run the program but for debugging purposes I use the app.run().
EDIT 2
Here is my updated code when I attempted to implement redis along with waitress
app = Flask(__name__)
app.secret_key = secrets.token_hex(16)
app.config['UPLOAD_FOLDER'] = 'uploads'
app.config['SESSION_TYPE'] = 'redis'
redis_client = redis.Redis()
app.config['SESSION_REDIS'] = redis.from_url('redis://localhost:6379')
app.config['SESSION_COOKIE_SECURE'] = True
Session(app)
def upload():
pdf_files = request.files.getlist('pdf_files')
if pdf_files:
if not redis_client.exists('user_folder:' + session.sid):
user_folder = str(uuid.uuid4())
user_upload_folder = os.path.join(app.config['UPLOAD_FOLDER'], user_folder)
D_user_upload_folder = user_upload_folder
os.makedirs(user_upload_folder, exist_ok=True)
# Store the user folder in Redis
test = redis_client.set('user_folder:' + session.sid, user_upload_folder)
print("Filename: ", test)
else:
user_upload_folder = redis_client.get('user_folder:' + session.sid)
D_user_upload_folder = user_upload_folder.decode()
#user_upload_folder = os.path.join(app.config['UPLOAD_FOLDER'], user_folder)
for pdf_file in pdf_files:
filename = secure_filename(pdf_file.headers['Content-Disposition'].split('filename=')[1].strip('"'))
file_path = os.path.join(D_user_upload_folder, filename)
pdf_file.save(file_path)
#Expermienting checking maybe the subprocesses were uploading the files simultaneously
if len(pdf_files) == 1:
return jsonify({'message': 'File uploaded successfully.'}), 200
else:
# Call the upload function again for the remaining files
remaining_files = pdf_files[1:]
with app.test_client() as client:
client.post('/upload', data={'pdf_files': remaining_files})
The code seemed to work fine on the local machine but when I tested it on a remote computer. Multiple folders were created again per each file
2
Answers
So, I have already managed to solve my problem.
Redis is part of the solution as it allows the data I have to be synchronized when used in other computers.
But it does not stop the problem of waitress making multiple calls in one function which is the root cause and reason why every time I upload a file multiple folders are created.
To solve this problem I first made sure that the creation of the folder is called during the creation of the main html page (In my case it is called "index()")
I created a global variable just to save me the hassle of passing parameters of the current name of the folder.
Since waitress creates multiple workers with each of their own unique identities. I used a thread-local variable to check if the thread has already executed the function.
This allowed me, to check if the function has already been executed and along with making the execution of this function at the initial creation of the main page, it has allowed me to prevent the multiple workers from creating multiple folders when called.
The problem lies in the fact that Waitress, the WSGI server you’re using, can spawn multiple worker processes to handle incoming requests. Each worker process has its own memory space and session storage, which can result in multiple folders being created for the same session.
Try using redis: