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

Heroku部署Flask应用报FileNotFoundError: [Errno 2]问题求助

问题描述

你正在开发托管在Heroku平台的Flask应用,核心需求是读写本地存储的文件:

  • 本地开发环境运行正常,可正常访问指定目录/project/files下的文件
  • 部署到Heroku生产环境后,访问本地目录持续抛出如下报错:
FileNotFoundError: [Errno 2] No such file or directory

相关业务代码

s = sftp.Connection(host=SFTP_HOSTNAME, username=SFTP_USERNAME, password=SFTP_PASSWORD, cnopts=cnopts)
    localFilePath = '/app/app/sftpfiles'
    remoteFilePath = '/files/client'
    
    files = s.listdir_attr(remoteFilePath)
    if files == []:
        return "empty"
    else:
        for f in files:
            if f.filename.endswith('.csv'):
                randomint = str(random.randint(1000,9999))
                datenow = str(datetime.now().date())
                lPath = localFilePath + '/' + f.filename
                newlPath = localFilePath + '/' + f.filename + '-' + datenow + '-' + randomint
                s.get(remoteFilePath + '/'  + f.filename, lPath)
                os.rename(lPath, newlPath)
                [...]

运行日志

2022-07-13T09:16:34.603426+00:00 app[web.1]: Connection succesfully established...
2022-07-13T09:16:34.603590+00:00 app[web.1]: Root Path:  /app/app
2022-07-13T09:16:34.603669+00:00 app[web.1]: CONNECTION IS:  <pysftp.Connection object at 0x7f93566e5960>
2022-07-13T09:16:34.706901+00:00 app[web.1]: Connection succesfully established...
2022-07-13T09:16:34.707053+00:00 app[web.1]: Root Path:  /app/app
2022-07-13T09:16:34.707092+00:00 app[web.1]: CONNECTION IS:  <pysftp.Connection object at 0x7f9578c6b30>
2022-07-13T09:16:35.926913+00:00 app[web.1]: BEFORE DOWNLOADING THE FILE app/sftpfiles/testagainok.csv-2022-07-13-6651
2022-07-13T09:16:35.943975+00:00 app[web.1]: BEFORE DOWNLOADING THE FILE app/sftpfiles/testagainok.csv-2022-07-13-9316
2022-07-13T09:16:36.082532+00:00 app[web.1]: AFTER DOWNLOADING THE FILE, HERE IS THE RESPONSE:
2022-07-13T09:16:36.094290+00:00 app[web.1]: Job "sync_work_days (trigger: interval[0:00:05], next run at: 2022-07-13 09:16:39 UTC)" raised an exception
2022-07-13T09:16:36.094292+00:00 app[web.1]: Traceback (most recent call last):
2022-07-13T09:16:36.094292+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/apscheduler/executors/base.py", line 125, in run_job
2022-07-13T09:16:36.094295+00:00 app[web.1]: retval = job.func(*job.args, **job.kwargs)
2022-07-13T09:16:36.094296+00:00 app[web.1]: File "/app/app/sftp.py", line 32, in sync_work_days
2022-07-13T09:16:36.094296+00:00 app[web.1]: s.get(remoteFilePath + '/'  + f.filename, lPath)
2022-07-13T09:16:36.094297+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/pysftp/__init__.py", line 249, in get
2022-07-13T09:16:36.094297+00:00 app[web.1]: self._sftp.get(remotepath, localpath, callback=callback)
2022-07-13T09:16:36.094297+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/paramiko/sftp_client.py", line 812, in get
2022-07-13T09:16:36.094298+00:00 app[web.1]: s = os.stat(localpath)
2022-07-13T09:16:36.094298+00:00 app[web.1]: FileNotFoundError: [Errno 2] No such file or directory: 'app/sftpfiles/testagainok.csv'
2022-07-13T09:16:39.610429+00:00 app[web.1]: Connection succesfully established...
2022-07-13T09:16:39.610592+00:00 app[web.1]: Root Path:  /app/app
2022-07-13T09:16:39.610658+00:00 app[web.1]: CONNECTION IS:  <pysftp.Connection object at 0x7f93568520b0>
2022-07-13T09:16:39.692308+00:00 app[web.1]: Connection succesfully established...
2022-07-13T09:16:39.692435+00:00 app[web.1]: Root Path:  /app/app

排查过程

  • 目标访问目录为/app/app/sftpfiles,目录结构如下:
    Heroku应用目录结构截图
  • 添加测试代码尝试直接读取指定路径文件:
print("TRYING OPENING A FILE")
f = open("/app/app/sftpfiles/testfile.csv")
print("results of the file are: ", f)
  • 通过命令touch /app/app/sftpfiles/testfile.csv手动创建测试文件,文件存在截图如下:
    手动创建的测试文件截图
  • 重新运行应用后仍然抛出相同报错:
2022-07-13T13:19:15.443344+00:00 app[web.1]: f = open("/app/app/sftpfiles/testfile.csv")
2022-07-13T13:19:15.443345+00:00 app[web.1]: FileNotFoundError: [Errno 2] No such file or directory: '/app/app/sftpfiles/testfile.csv'
问题原因

核心问题来自Heroku平台的运行机制和代码路径处理的疏漏:

  • Heroku的dyno文件系统是临时、非持久化的,每次部署、重启、缩放实例时都会完全重置文件系统。通过heroku run bash手动执行touch创建的文件,只会存在于那次命令启动的一次性独立dyno中,和实际运行web服务的dyno是完全隔离的环境,web服务所在的dyno里根本不存在手动创建的文件
  • Git本身不会跟踪空目录,如果代码仓库里的sftpfiles是空目录、没有提交任何占位文件,部署到Heroku时这个目录不会被创建,访问时自然会报路径不存在
  • Heroku dyno的文件系统除了/tmp临时目录外其他路径都是只读的,即使目录存在,写入的文件也会在dyno生命周期结束时被完全清除,无法持久保存
  • 从报错日志能看到路径不一致问题:代码里定义的是绝对路径/app/app/sftpfiles,但实际报错时访问的是相对路径app/sftpfiles/testagainok.csv,说明代码运行时工作目录和预期不符,手动拼接字符串生成路径的方式很容易出现这类解析错误。
修复方案
  • 不要依赖Heroku本地文件系统做持久化存储:如果需要长期保存下载的文件,替换为第三方持久化存储服务,比如AWS S3、Heroku自带的Postgres/Redis或其他对象存储服务,文件下载后直接上传到对象存储,不要存在本地磁盘
  • 如果只是临时处理文件(处理完就删除、不需要持久保存),统一使用/tmp目录作为临时文件存储路径,这个目录在dyno运行期间是可写的,启动时自动创建目标目录避免不存在报错:
    import os
    localFilePath = '/tmp/sftpfiles'
    os.makedirs(localFilePath, exist_ok=True)
    
  • 路径拼接不要手动用字符串加/,用os.path.join避免跨平台、工作目录不一致导致的路径错误:
    lPath = os.path.join(localFilePath, f.filename)
    newlPath = os.path.join(localFilePath, f"{f.filename}-{datenow}-{randomint}")
    remote_file_full_path = os.path.join(remoteFilePath, f.filename)
    s.get(remote_file_full_path, lPath)
    
  • 不要通过heroku run bash手动创建文件验证路径,这种方式启动的是独立的一次性dyno,和运行web服务的dyno文件系统不互通,验证逻辑要直接写在应用代码中,比如启动时打印目标目录的文件列表。

注意:存放在/tmp目录的文件会在dyno停止、重启、重新部署时被完全删除,仅适合存放临时处理的文件,绝对不能存需要持久保留的业务数据。

内容的提问来源于stack exchange,提问作者Kaiss B.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 11:36:21