不一致的Flask会话

huangapple go评论68阅读模式
英文:

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.

huangapple
  • 本文由 发表于 2023年7月13日 13:18:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/76676145.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定