[Python] Flask helper function not saving files to app.config['UPLOAD_FOLDER'] path

Discussão em 'Python' iniciado por Stack, Junho 2, 2025.

  1. Stack

    Stack Membro Participativo

    I'm working on a Flask application and have created a file_handler.py file to organize some utility functions. One of the functions in helper.py is supposed to save an uploaded file to the folder specified by app.config['UPLOAD_FOLDER'] defined in app.py.

    To access the upload folder path, I'm passing app.config['UPLOAD_FOLDER'] as an argument to the function. However, the file doesn’t actually get saved to the specified folder, and no error is raised by the system. Though my error handling raises the ValueError: "No content available for processing".

    Additionally, I have another function in helper.py called file_loader() that uses Flask's request object to fetch data. Since helper.py is a separate file, I’m passing the request object to it as an argument as well. I'm wondering if this might be causing an issue—could the function be failing silently because it can’t properly access the host or context from another module?

    Here’s a simplified version of the setup:

    app.py

    from file_handler import File
    import logging

    load_dotenv()

    app = Flask(__name__)
    app.config['UPLOAD_FOLDER'] = 'uploads'
    os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)

    files = File(app)

    @app.route('/', methods=['GET', 'POST'])
    def index():
    result = ''

    if request.method == 'POST':
    query = request.form.get('query', '').lower().strip()
    text = []

    input_groups = set()
    for key in request.form.keys():
    if key.startswith('file_type_'):
    group_id = key.split('_')[2]
    input_groups.add(group_id)

    for group_id in sorted(input_groups):
    file_type = request.form.get(f'file_type_{group_id}')
    if file_type:
    try:
    content = File.file_loader(file_type, request, group_id)
    text.extend(content)
    except Exception as e:
    print(f"Error processing input {group_id}: {e}")

    if len(text)==0:
    raise ValueError("No content available for processing")

    return render_template('index.html', result=result)


    file_handler.py

    class File():
    def __init__(self, app):
    self.SUPPORTED_FILE_TYPES = {}
    self.app = app

    def validate_file_type(self, filename, expected_type):
    """Validate if the uploaded file matches the expected type"""
    if not filename:
    return False, "No filename provided"

    ext = filename.rsplit('.', 1)[-1].lower() if '.' in filename else ''

    if not ext:
    return False, "File has no extension"

    if ext not in self.SUPPORTED_FILE_TYPES:
    return False, f"Unsupported file extension: .{ext}"

    if expected_type == 'text' and ext != 'txt':
    return False, "Please upload a .txt file for Text File type"
    elif expected_type == 'pdf' and ext != 'pdf':
    return False, "Please upload a .pdf file for PDF File type"
    elif expected_type == 'code' and ext in ['txt', 'pdf']:
    return False, "Please upload a programming file for Code File type"

    return True, ext

    def file_save(self, file, expected_type):
    """Save uploaded file with proper validation"""
    if not file or file.filename == '':
    return None, "No file selected"

    is_valid, result = self.validate_file_type(file.filename, expected_type)
    if not is_valid:
    return None, result

    ext = result
    secure_name = secure_filename(file.filename)
    file_path = os.path.join(self.app.config['UPLOAD_FOLDER'], secure_name)

    try:
    file.save(file_path)
    return file_path, ext
    except Exception as e:
    return None, f"Failed to save file: {str(e)}"

    @staticmethod
    def validate_url(url):
    """Validate URL format"""
    if not url or not url.strip():
    return False, "URL cannot be empty"

    url = url.strip()
    if not url.startswith(('http://', 'https://')):
    url = 'https://' + url

    if not validators.url(url):
    return False, "Invalid URL format"

    return True, url

    def file_loader(self, file_type, request, group_id):
    """Load and process different file types"""
    content = ''
    splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=30)


    if file_type == 'text':
    file_path, _ = self.file_save(request.files.get(f'file_{group_id}'), 'text')
    loader = TextLoader(file_path)

    elif file_type == 'pdf':
    file_path, _ = self.file_save(request.files.get(f'file_{group_id}'), 'pdf')
    loader = PyMuPDFLoader(file_path, mode="page", images_inner_format="text", images_parser=RapidOCRBlobParser())

    elif file_type == 'code':
    file_path, ext = self.file_save(request.files.get(f'file_{group_id}'), 'code')
    if ext != 'ipynb':
    loader = TextLoader(file_path)
    try:
    splitter = splitter.from_language(self.SUPPORTED_FILE_TYPES[ext])
    except:
    pass
    else:
    loader = NotebookLoader(file_path, include_outputs=True, max_output_length=30)

    elif file_type == 'link':
    url = request.form.get(f'url_{group_id}', '').strip()
    is_valid, validated_url = self.validate_url(url)
    if not is_valid:
    raise ValueError(validated_url)

    loader = WebBaseLoader(validated_url)

    elif file_type == 'pasted':
    content = request.form.get(f'pasted_{group_id}', '').strip()
    if not content:
    raise ValueError("No text was pasted")

    try:
    for page in loader.lazy_load():
    content += page.page_content + '\n'

    except Exception as e:
    raise ValueError(f"Could not load content from file: {str(e)}")

    if not content.strip():
    raise ValueError("The file appears to be empty or contains no readable content")

    chunks = splitter.split_text(content)

    return chunks


    Questions:


    1. Could passing request into a helper function like this cause issues with accessing form data or headers?


    2. Is there a reason why file.save() might silently fail when called from another file, even though the path is passed correctly from app.config['UPLOAD_FOLDER']?

    I’ve confirmed that the folder exists and is writable. And this works fine if these codes are present in the same file.

    Any insights would be appreciated!

    Continue reading...

Compartilhe esta Página