Flask应用部署Heroku后无法更新CSV,求助写入GitHub托管CSV
解决Heroku上CSV无法持久化&向GitHub托管CSV追加内容的方案
首先,先解释你在Heroku上遇到的CSV不更新问题:Heroku使用临时文件系统(Ephemeral Filesystem),所有对本地文件的修改只在当前dyno的生命周期内有效——当dyno重启(比如部署更新、自动缩放、睡眠唤醒),这些修改就会丢失。而且多个dyno之间的文件系统是独立的,所以即使看起来运行正常,数据也不会持久化,这就是你克隆仓库看不到更新的原因。
接下来解决你现在的核心需求:向GitHub托管的CSV文件追加内容。GitHub本身不是数据库,但我们可以通过GitHub REST API来实现修改仓库里的CSV文件,具体步骤如下:
1. 准备GitHub Personal Access Token (PAT)
你需要生成一个带仓库权限的PAT,用来通过API修改你的仓库:
- 登录GitHub,进入「Settings」→「Developer settings」→「Personal access tokens」
- 点击「Generate new token」,勾选
repo权限(需要完整的仓库读写权限) - 生成后复制保存好这个token(只显示一次)
2. 用Python实现追加功能
我们可以用requests库调用GitHub API,结合Pandas处理CSV格式,确保数据格式正确。
步骤说明:
- 调用API获取当前CSV文件的信息(包括唯一的
sha值,GitHub需要它确认修改的是最新版本) - 解码文件内容,用Pandas加载并追加新数据
- 将更新后的内容编码为base64(GitHub API要求的格式)
- 发送PUT请求更新文件
完整代码示例
import os import requests import pandas as pd import base64 from io import StringIO # 从环境变量获取token,避免硬编码(Heroku上用heroku config:set GITHUB_TOKEN=你的token设置) GITHUB_TOKEN = os.getenv("GITHUB_TOKEN") REPO_OWNER = "parthpandyappp" REPO_NAME = "Saturday-Talks" FILE_PATH = "registered.csv" API_URL = f"https://api.github.com/repos/{REPO_OWNER}/{REPO_NAME}/contents/{FILE_PATH}" def append_name_to_github_csv(new_name): # 1. 获取当前CSV文件的元数据和内容 headers = {"Authorization": f"token {GITHUB_TOKEN}"} try: response = requests.get(API_URL, headers=headers) response.raise_for_status() file_data = response.json() except requests.exceptions.RequestException as e: print(f"获取文件失败: {e}") return None # 2. 解码当前CSV内容并加载为DataFrame current_content = base64.b64decode(file_data["content"]).decode("utf-8") df = pd.read_csv(StringIO(current_content)) # 3. 追加新名称(假设CSV的列名为"name",如果不是请修改) new_row = pd.DataFrame({"name": [new_name]}) df = pd.concat([df, new_row], ignore_index=True) # 4. 将更新后的内容转为CSV字符串并编码为base64 updated_csv = df.to_csv(index=False) updated_content_b64 = base64.b64encode(updated_csv.encode("utf-8")).decode("utf-8") # 5. 发送更新请求到GitHub API payload = { "message": f"Register new participant: {new_name}", "content": updated_content_b64, "sha": file_data["sha"] # 必须传入当前文件的sha,否则会报错 } try: update_response = requests.put(API_URL, json=payload, headers=headers) update_response.raise_for_status() print("名称追加成功!") return update_response.json() except requests.exceptions.RequestException as e: print(f"更新文件失败: {e}") return None # 在Flask路由中调用这个函数 # @app.route("/submit", methods=["POST"]) # def submit_name(): # name = request.form.get("name") # if name: # append_name_to_github_csv(name) # return "提交成功!" # return "请输入名称!"
3. 关键注意事项
- 不要硬编码token:在Heroku上用
heroku config:set GITHUB_TOKEN=你的PAT设置环境变量,代码里用os.getenv获取,避免泄露敏感信息 - 处理并发冲突:如果多个用户同时提交,可能会出现409 Conflict错误(因为获取sha和更新之间文件被修改了),可以添加重试逻辑解决
- GitHub API速率限制:免费用户每小时最多5000次请求,小应用完全足够,大流量场景建议改用数据库(比如Heroku Postgres)
- CSV列名匹配:确保代码里的列名和你的
registered.csv一致,如果你的CSV有多列,调整pd.DataFrame的字段即可
内容的提问来源于stack exchange,提问作者Parth Pandya
相关产品推荐
相关产品推荐

