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

Flask+Heroku部署的URL短链工具偶发‘URL未找到’问题求助

问题根源分析

你的猜测完全正确,核心问题就是内存字典shortened_urls的临时存储特性,加上部署环境的特殊机制导致的:

  • Heroku的Dyno是无状态的,会定期重启、自动缩放或者在闲置时休眠,每次重启后内存中的所有数据都会被清空,所以之前生成的短链映射会丢失。
  • 你写的从urls.json加载数据的逻辑只在if __name__ == "__main__"分支里执行,但Heroku部署Flask应用时,通常用Gunicorn等WSGI服务器启动,不会触发这个分支,所以即使有持久化的json文件,启动时也不会加载。
  • 另外你注释了写入urls.json的代码,就算本地运行也没把数据持久化到文件,部署后更不可能保留数据。

Homebrew安装Heroku CLI的警告和这个问题无关,可以忽略。

解决方案

方案1:临时修复(用文件持久化,仅用于测试)

先修复文件读写的逻辑,让数据能正确持久化到文件,并且启动时自动加载:

修改后的main.py代码:

import random
import string
import json
import os

from flask import Flask, render_template, redirect, request

app = Flask(__name__)
# 定义json文件路径
URLS_FILE = "urls.json"
shortened_urls = {}

def load_urls():
    """启动时加载已保存的短链数据"""
    if os.path.exists(URLS_FILE):
        with open(URLS_FILE, "r") as f:
            return json.load(f)
    return {}

def save_urls():
    """保存当前短链数据到文件"""
    with open(URLS_FILE, "w") as f:
        json.dump(shortened_urls, f)

def generate_short_url(length=6):
    chars = string.ascii_letters + string.digits
    short_url = "".join(random.choice(chars) for _ in range(length))
    return short_url

# 启动时加载数据
shortened_urls = load_urls()

@app.route("/", methods=["GET", "POST"])
def index():
    if request.method == "POST":
        long_url = request.form['long_url']
        short_url = generate_short_url()
        while short_url in shortened_urls:
            short_url = generate_short_url()

        shortened_urls[short_url] = long_url
        save_urls()  # 新增链接后立即保存
        full_short_url = request.url_root + short_url
        return render_template("index.html", short_url=full_short_url)

    return render_template("index.html")

@app.route("/<short_url>")
def redirect_url(short_url):
    long_url = shortened_urls.get(short_url)
    if long_url:
        return redirect(long_url)
    else:
        return "URL not found", 404

if __name__ == "__main__":
    app.run(debug=True)

⚠️ 注意:Heroku的文件系统是临时的,每次Dyno重启后,urls.json会被重置为部署时的状态,所以这个方案只能用于临时测试,不能作为生产环境的解决方案。

方案2:生产环境推荐(用数据库持久化)

Heroku提供免费的PostgreSQL数据库服务,用SQLAlchemy来连接数据库,实现真正的持久化存储:

步骤1:安装依赖

在requirements.txt中添加:

flask
flask-sqlalchemy
psycopg2-binary
gunicorn

步骤2:修改main.py使用SQLAlchemy

import random
import string
from flask import Flask, render_template, redirect, request
from flask_sqlalchemy import SQLAlchemy
import os

app = Flask(__name__)
# 配置数据库,Heroku会自动设置DATABASE_URL环境变量
app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv('DATABASE_URL', 'sqlite:///urls.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

# 定义URL映射模型
class URLMapping(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    short_url = db.Column(db.String(6), unique=True, nullable=False)
    long_url = db.Column(db.String(500), nullable=False)

def generate_short_url(length=6):
    chars = string.ascii_letters + string.digits
    short_url = "".join(random.choice(chars) for _ in range(length))
    # 检查是否已存在
    while URLMapping.query.filter_by(short_url=short_url).first():
        short_url = generate_short_url()
    return short_url

# 创建数据库表(首次运行时需要)
with app.app_context():
    db.create_all()

@app.route("/", methods=["GET", "POST"])
def index():
    if request.method == "POST":
        long_url = request.form['long_url']
        short_url = generate_short_url()
        # 保存到数据库
        new_url = URLMapping(short_url=short_url, long_url=long_url)
        db.session.add(new_url)
        db.session.commit()
        
        full_short_url = request.url_root + short_url
        return render_template("index.html", short_url=full_short_url)

    return render_template("index.html")

@app.route("/<short_url>")
def redirect_url(short_url):
    url_entry = URLMapping.query.filter_by(short_url=short_url).first()
    if url_entry:
        return redirect(url_entry.long_url)
    else:
        return "URL not found", 404

if __name__ == "__main__":
    app.run(debug=True)

步骤3:部署到Heroku

  1. 确保你已经登录Heroku CLI,创建应用并关联数据库:
heroku create your-app-name
heroku addons:create heroku-postgresql:hobby-dev
  1. 提交代码并部署:
git add .
git commit -m "Switch to PostgreSQL for persistent storage"
git push heroku main

这样修改后,短链数据会持久化到PostgreSQL数据库,即使Dyno重启,数据也不会丢失,彻底解决问题。

内容的提问来源于stack exchange,提问作者nc__33

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 20:22:40