You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python-Bottle上传S3及写入PostgreSQL RDS时出现500内部服务器错误

问题排查:添加PostgreSQL读写后Bottle服务上传S3失败触发500错误

问题概述

在EC2实例部署的Python-Bottle服务,原本可正常将图片上传至S3存储桶;添加PostgreSQL RDS读写逻辑后,上传操作触发500内部服务器错误,文件仅保存至EC2本地user_uploads目录,未上传至S3。已确认EC2与S3、RDS的通信规则均开放。

数据库建表语句:

CREATE SCHEMA bottletube;
SET SCHEMA 'bottletube';

CREATE TABLE IF NOT EXISTS image_uploads
(
    id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    url VARCHAR(20) NOT NULL,
    category VARCHAR(64)
);

相关Python核心代码(简化后):

# 上传路由逻辑
@route('/upload', method='POST')
def do_upload_post():
    # ... 字段校验、本地文件保存逻辑 ...

    # 上传至S3
    data = open(SAVE_PATH + save_filename, 'rb')
    s3_resource.Bucket(BUCKET_NAME).put_object(Key=f'user_uploads/{save_filename}',
                                               Body=data,
                                               Metadata={'Content-Type': content_type},
                                               ACL='public-read')

    # 写入数据库
    cursor.execute(f"INSERT INTO image_uploads (url, category) VALUES ('user_uploads/{save_filename}', '{category}');")
    connection.commit()

核心问题排查与修复

1. 数据库字段长度不足(直接触发500错误)

你的image_uploads表中url字段定义为VARCHAR(20),但插入的user_uploads/{save_filename}字符串长度远超20(例如user_uploads/photo_20240520.jpg就有27个字符),插入时会触发字段值过长的数据库错误,导致事务提交失败,抛出未捕获异常,最终Bottle返回500错误。

修复方法:修改url字段长度为足够容纳S3对象路径的长度:

ALTER TABLE bottletube.image_uploads ALTER COLUMN url TYPE VARCHAR(255);

2. SQL字符串拼接导致语法错误与注入风险

直接用f-string拼接SQL语句,当category包含单引号(如'nature's beauty')或特殊字符时,会触发SQL语法错误,同时存在SQL注入风险,这也是引发500错误的常见原因。

修复方法:使用参数化查询代替字符串拼接:

# 替换原INSERT语句
cursor.execute(
    "INSERT INTO image_uploads (url, category) VALUES (%s, %s);",
    (f'user_uploads/{save_filename}', category)
)
connection.commit()

3. 路径拼接错误导致S3读取文件失败

当前代码中SAVE_PATH + save_filename未处理路径分隔符(若SAVE_PATH为user_uploads且无末尾斜杠,会拼接成user_uploadstest.jpg),导致本地文件路径错误,S3读取文件时触发异常,中断上传流程。

修复方法:使用os.path.join安全拼接路径:

# 本地文件保存路径
local_file_path = os.path.join(SAVE_PATH, save_filename)
with open(local_file_path, 'wb') as open_file:
    open_file.write(upload.file.read())

# S3读取路径
data = open(local_file_path, 'rb')

4. 缺失异常捕获与日志输出

当前代码在S3上传、DB写入步骤未添加异常捕获,无法定位具体错误原因。建议添加异常捕获并打印日志,方便快速排查:

# S3上传部分添加异常捕获
try:
    data = open(local_file_path, 'rb')
    s3_resource.Bucket(BUCKET_NAME).put_object(Key=f'user_uploads/{save_filename}',
                                               Body=data,
                                               Metadata={'Content-Type': content_type},
                                               ACL='public-read')
except Exception as e:
    print(f"S3上传失败: {str(e)}")
    error_messages.append(f"上传至S3失败: {str(e)}")
    return template('upload.tpl', name='Upload Image', error_messages=error_messages)
finally:
    data.close()

# DB写入部分添加异常捕获
try:
    cursor.execute(
        "INSERT INTO image_uploads (url, category) VALUES (%s, %s);",
        (f'user_uploads/{save_filename}', category)
    )
    connection.commit()
except Exception as e:
    connection.rollback()
    print(f"数据库写入失败: {str(e)}")
    error_messages.append(f"保存至数据库失败: {str(e)}")
    return template('upload.tpl', name='Upload Image', error_messages=error_messages)

5. 全局数据库连接/游标线程安全隐患

Bottle默认单线程运行,但全局的connection和cursor在后续切换多线程服务器(如waitress)时会引发线程安全问题,甚至导致随机错误。

优化建议:在每个请求中创建局部数据库连接/游标,或使用连接池:

@route('/home')
@route('/')
def home():
    items = []
    # 使用局部连接与游标
    with psycopg2.connect(user="postgres", host="your-rds-endpoint", password="your-password", database="bottletube") as conn:
        with conn.cursor() as cur:
            cur.execute('SET SCHEMA \'bottletube\'')
            cur.execute('SELECT * FROM image_uploads ORDER BY id')
            for record in cur.fetchall():
                items.append({'id': record[0], 'filename': record[1], 'category': record[2]})
    return template('home.tpl', name='BoTube Home', items=items)

验证步骤

  1. 先修改数据库url字段长度为255
  2. 替换SQL语句为参数化查询
  3. 修复路径拼接逻辑
  4. 添加异常捕获与日志打印
  5. 重启Bottle服务后重新测试上传

内容的提问来源于stack exchange,提问作者Oliver-René Goltsche

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 06:01:45