英文:
Inconsistent Flask Session
问题
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()
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
英文:
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
答案1
得分: 0
问题出在你使用的WSGI服务器Waitress,它可以生成多个工作进程来处理传入的请求。每个工作进程都有自己的内存空间和会话存储,这可能导致为同一个会话创建多个文件夹。
尝试使用redis:
import redis
....
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_REDIS'] = redis.from_url('redis://localhost:6379')
英文:
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:
import redis
....
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_REDIS'] = redis.from_url('redis://localhost:6379')
答案2
得分: 0
已经成功解决了我的问题。
Redis是解决方案的一部分,因为它允许在其他计算机中使用时同步我拥有的数据。
但它无法阻止服务员在一个函数中进行多次调用,这是导致每次上传文件时都会创建多个文件夹的根本原因。
为了解决这个问题,我首先确保在创建主HTML页面时调用了文件夹的创建(在我的情况下它被称为“index()”)。
@app.route('/')
def index():
global current_folder
create_user_folder()
return render_template('index.html', filenames=[])
我创建了一个全局变量,只是为了免去传递当前文件夹名称的麻烦。
由于服务员创建了多个具有各自唯一标识的工作进程,我使用了线程本地变量来检查线程是否已经执行了该函数。
import threading
# 创建一个线程本地变量,用于存储当前线程内是否已执行该函数的信息
thread_local = threading.local()
def create_user_folder():
global current_folder
# 检查当前用户在当前线程内是否已执行该函数
if hasattr(thread_local, 'user_folder'):
session['user_folder'] = thread_local.user_folder
return
'''
更多代码,用于检查是否存在用户ID和已创建的文件夹
如果不存在用户文件夹,然后将其创建并分配给各自的变量。
'''
这使我能够检查函数是否已经执行,并且在主页面的初始创建时执行此函数,从而防止多个工作进程在调用时创建多个文件夹。
英文:
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()")
@app.route('/')
def index():
global current_folder
create_user_folder()
return render_template('index.html', filenames=[])
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.
import threading
# Create a thread-local variable to store whether the function has been executed for the current user within the thread
thread_local = threading.local()
def create_user_folder():
global current_folder
# Check if the function has already been executed for the current user in the current thread
if hasattr(thread_local, 'user_folder'):
session['user_folder'] = thread_local.user_folder
return
'''
More code that checks if there is user_id and folder already created
if not create the folder and assign them to their respected variables.
'''
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论