Python实例题:基于 Flask 的在线代码编辑器

目录

Python实例题

题目

问题描述

解题思路

关键代码框架

模板文件示例

难点分析

扩展方向

Python实例题

题目

基于 Flask 的在线代码编辑器

问题描述

开发一个简单的在线代码编辑器,支持以下功能:

  • 创建、编辑和保存多种编程语言的代码文件
  • 提供语法高亮显示
  • 实现代码实时编译和运行(支持 Python、JavaScript 等)
  • 管理多个项目和文件
  • 用户认证和权限管理

解题思路

  • 使用 Flask 框架构建 Web 应用
  • 实现文件管理系统(创建、读取、更新、删除文件)
  • 集成 CodeMirror 实现代码编辑功能
  • 设计沙箱环境执行代码
  • 使用 Flask-Login 实现用户认证

关键代码框架

import os
import uuid
import subprocess
import tempfile
from flask import Flask, render_template, request, jsonify, redirect, url_for, session
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
from werkzeug.security import generate_password_hash, check_password_hash
from flask_socketio import SocketIO, send, emit

# 配置
UPLOAD_FOLDER = 'projects'
ALLOWED_EXTENSIONS = {'py', 'js', 'html', 'css', 'java', 'c', 'cpp'}
MAX_FILE_SIZE = 10 * 1024 * 1024  # 10MB

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-here'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = MAX_FILE_SIZE

# 确保项目目录存在
os.makedirs(UPLOAD_FOLDER, exist_ok=True)

# 初始化Flask-Login
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'

# 初始化SocketIO
socketio = SocketIO(app)

# 用户模型
class User(UserMixin):
    def __init__(self, id, username, password_hash):
        self.id = id
        self.username = username
        self.password_hash = password_hash
    
    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

# 模拟用户数据库
users = {}

# 用户加载回调
@login_manager.user_loader
def load_user(user_id):
    return users.get(user_id)

# 辅助函数
def allowed_file(filename):
    """检查文件扩展名是否允许"""
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

def get_project_dir(username):
    """获取用户项目目录"""
    return os.path.join(UPLOAD_FOLDER, username)

def get_project_file_path(username, project_name, filename):
    """获取项目文件完整路径"""
    return os.path.join(get_project_dir(username), project_name, filename)

def create_user(username, password):
    """创建新用户"""
    user_id = str(uuid.uuid4())
    password_hash = generate_password_hash(password)
    user = User(user_id, username, password_hash)
    users[user_id] = user
    
    # 创建用户项目目录
    os.makedirs(get_project_dir(username), exist_ok=True)
    
    return user

def create_project(username, project_name):
    """创建新项目"""
    project_dir = os.path.join(get_project_dir(username), project_name)
    os.makedirs(project_dir, exist_ok=True)
    return project_dir

def get_projects(username):
    """获取用户所有项目"""
    user_dir = get_project_dir(username)
    if not os.path.exists(user_dir):
        return []
    
    return [d for d in os.listdir(user_dir) if os.path.isdir(os.path.join(user_dir, d))]

def get_project_files(username, project_name):
    """获取项目所有文件"""
    project_dir = os.path.join(get_project_dir(username), project_name)
    if not os.path.exists(project_dir):
        return []
    
    return [f for f in os.listdir(project_dir) if os.path.isfile(os.path.join(project_dir, f))]

def read_file_content(file_path):
    """读取文件内容"""
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            return f.read()
    except Exception as e:
        return f"读取文件失败: {str(e)}"

def write_file_content(file_path, content):
    """写入文件内容"""
    try:
        with open(file_path, 'w', encoding='utf-8') as f:
            f.write(content)
        return True
    except Exception as e:
        print(f"写入文件失败: {str(e)}")
        return False

# 路由
@app.route('/')
@login_required
def index():
    """主页"""
    projects = get_projects(current_user.username)
    return render_template('index.html', projects=projects)

@app.route('/register', methods=['GET', 'POST'])
def register():
    """注册页面"""
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        
        # 检查用户名是否已存在
        for user in users.values():
            if user.username == username:
                return render_template('register.html', error='用户名已存在')
        
        # 创建新用户
        user = create_user(username, password)
        login_user(user)
        return redirect(url_for('index'))
    
    return render_template('register.html')

@app.route('/login', methods=['GET', 'POST'])
def login():
    """登录页面"""
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        
        # 查找用户
        for user in users.values():
            if user.username == username and user.check_password(password):
                login_user(user)
                return redirect(url_for('index'))
        
        return render_template('login.html', error='用户名或密码错误')
    
    return render_template('login.html')

@app.route('/logout')
@login_required
def logout():
    """登出"""
    logout_user()
    return redirect(url_for('login'))

@app.route('/project/create', methods=['POST'])
@login_required
def create_new_project():
    """创建新项目"""
    project_name = request.form['project_name']
    if not project_name:
        return jsonify({'success': False, 'message': '项目名称不能为空'})
    
    create_project(current_user.username, project_name)
    return jsonify({'success': True, 'message': '项目创建成功'})

@app.route('/project/')
@login_required
def project_detail(project_name):
    """项目详情页"""
    files = get_project_files(current_user.username, project_name)
    return render_template('project.html', project_name=project_name, files=files)

@app.route('/file/create', methods=['POST'])
@login_required
def create_new_file():
    """创建新文件"""
    project_name = request.form['project_name']
    filename = request.form['filename']
    
    if not allowed_file(filename):
        return jsonify({'success': False, 'message': '不支持的文件类型'})
    
    file_path = get_project_file_path(current_user.username, project_name, filename)
    
    # 检查文件是否已存在
    if os.path.exists(file_path):
        return jsonify({'success': False, 'message': '文件已存在'})
    
    # 创建文件
    try:
        with open(file_path, 'w') as f:
            f.write('')
        return jsonify({'success': True, 'message': '文件创建成功'})
    except Exception as e:
        return jsonify({'success': False, 'message': f'创建文件失败: {str(e)}'})

@app.route('/file//')
@login_required
def file_detail(project_name, filename):
    """文件详情页"""
    file_path = get_project_file_path(current_user.username, project_name, filename)
    content = read_file_content(file_path)
    
    # 获取文件扩展名,用于CodeMirror模式
    ext = filename.rsplit('.', 1)[1].lower() if '.' in filename else ''
    
    # 映射文件扩展名到CodeMirror模式
    mode_map = {
        'py': 'python',
        'js': 'javascript',
        'html': 'htmlmixed',
        'css': 'css',
        'java': 'clike',
        'c': 'clike',
        'cpp': 'clike'
    }
    
    mode = mode_map.get(ext, 'text')
    
    return render_template('editor.html', project_name=project_name, 
                           filename=filename, content=content, mode=mode)

@app.route('/file/save', methods=['POST'])
@login_required
def save_file():
    """保存文件"""
    project_name = request.form['project_name']
    filename = request.form['filename']
    content = request.form['content']
    
    file_path = get_project_file_path(current_user.username, project_name, filename)
    
    if write_file_content(file_path, content):
        return jsonify({'success': True, 'message': '文件保存成功'})
    else:
        return jsonify({'success': False, 'message': '文件保存失败'})

# 代码执行路由
@app.route('/execute', methods=['POST'])
@login_required
def execute_code():
    """执行代码"""
    code = request.json['code']
    language = request.json['language']
    
    # 创建临时文件
    with tempfile.TemporaryDirectory() as temp_dir:
        if language == 'python':
            file_path = os.path.join(temp_dir, 'main.py')
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(code)
            
            try:
                # 执行Python代码
                result = subprocess.run(
                    ['python3', file_path], 
                    capture_output=True, 
                    text=True,
                    timeout=10  # 限制执行时间为10秒
                )
                
                output = result.stdout
                error = result.stderr
                
                return jsonify({
                    'success': True,
                    'output': output,
                    'error': error
                })
            except subprocess.TimeoutExpired:
                return jsonify({
                    'success': False,
                    'error': '执行超时(超过10秒)'
                })
            except Exception as e:
                return jsonify({
                    'success': False,
                    'error': f'执行错误: {str(e)}'
                })
        
        elif language == 'javascript':
            file_path = os.path.join(temp_dir, 'main.js')
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(code)
            
            try:
                # 执行JavaScript代码
                result = subprocess.run(
                    ['node', file_path], 
                    capture_output=True, 
                    text=True,
                    timeout=10
                )
                
                output = result.stdout
                error = result.stderr
                
                return jsonify({
                    'success': True,
                    'output': output,
                    'error': error
                })
            except Exception as e:
                return jsonify({
                    'success': False,
                    'error': f'执行错误: {str(e)}'
                })
        
        else:
            return jsonify({
                'success': False,
                'error': f'暂不支持的语言: {language}'
            })

# SocketIO事件处理
@socketio.on('connect')
def handle_connect():
    """客户端连接事件"""
    print('客户端已连接')
    emit('connected', {'data': '连接成功'})

@socketio.on('disconnect')
def handle_disconnect():
    """客户端断开连接事件"""
    print('客户端已断开连接')

if __name__ == '__main__':
    # 创建测试用户
    if not users:
        create_user('test', 'test123')
    
    # 启动应用
    socketio.run(app, debug=True)

模板文件示例




    
    {{ filename }} - 代码编辑器
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    


    
CodePad
{{ project_name }}
{{ filename }}
{{ current_user.username }}
语言:
代码编辑器
输出结果

等待代码执行...

难点分析

  • 安全沙箱:实现安全的代码执行环境,防止恶意代码
  • 异步执行:处理长时间运行的代码而不阻塞服务器
  • 实时通信:使用 WebSocket 实现实时输出显示
  • 文件管理:设计可靠的文件存储和管理系统
  • 前端交互:实现流畅的代码编辑体验和响应式界面

扩展方向

  • 添加更多编程语言支持
  • 实现代码自动保存功能
  • 添加代码分享功能
  • 集成版本控制系统
  • 增加代码调试功能
  • 设计更美观的用户界面

你可能感兴趣的:(实例,python,flask,编辑器)