Heroku 的文件系统是只读且瞬态的,无法使用本地绝对路径(如 ./src/main/java/...)创建文件;必须通过环境变量指定可写临时目录,并动态创建子目录与文件。
heroku 的文件系统是只读且瞬态的,无法使用本地绝对路径(如 `./src/main/java/...`)创建文件;必须通过环境变量指定可写临时目录,并动态创建子目录与文件。
在 Java Web 应用(如 Spring Boot)部署到 Heroku 时,一个常见却易被忽视的陷阱是:硬编码的相对或绝对文件路径在本地运行正常,但在 Heroku 上必然失败。根本原因在于 Heroku 的运行时约束:
- ❌ 文件系统不可写(除 /tmp 及显式创建的子目录外):Heroku 的 slug 编译后,应用目录(如 target/, src/)处于只读状态;
- ❌ 工作目录不确定:System.getProperty("user.dir") 在 Heroku dyno 中可能指向 /app,但该路径不可写;
- ❌ 路径不存在即报错:new File("./src/main/java/org/test/resources/temp.json") 会尝试在只读路径下创建文件,触发 FileNotFoundException。
✅ 正确做法是:使用 Heroku 提供的可写临时路径(推荐 /tmp),或通过环境变量灵活配置。以下为生产就绪的实现方案:
✅ 推荐方案:优先使用 /tmp(Heroku 官方支持的临时目录)
private static File makeTempFile(String data, String fileName) {
// Heroku 保证 /tmp 是可写且持久化至 dyno 生命周期结束
File tempDir = new File("/tmp");
if (!tempDir.exists()) {
throw new IllegalStateException("/tmp directory not available");
}
File file = new File(tempDir, "temp_" + fileName + ".json");
try (FileWriter writer = new FileWriter(file)) {
writer.write(formatJson(data));
System.out.println("Created: " + file.getAbsolutePath());
return file;
} catch (IOException e) {
throw new RuntimeException("Failed to write temp file to /tmp", e);
}
}✅ 进阶方案:通过环境变量解耦路径(更灵活、便于多环境部署)
private static final String TEMP_DIR_ENV = "HEROKU_TEMP_DIR";
private static File makeTempFile(String data, String fileName) {
String tempDirPath = System.getenv(TEMP_DIR_ENV);
if (tempDirPath == null || tempDirPath.trim().isEmpty()) {
// 回退到 /tmp(Heroku 默认安全路径)
tempDirPath = "/tmp";
}
File dir = new File(tempDirPath);
if (!dir.exists() && !dir.mkdirs()) {
throw new RuntimeException("Failed to create temp directory: " + dir.getAbsolutePath());
}
File file = new File(dir, "temp_" + fileName + ".json");
try (FileWriter writer = new FileWriter(file)) {
writer.write(formatJson(data));
System.out.println("Created: " + file.getAbsolutePath());
return file;
} catch (IOException e) {
throw new RuntimeException("Failed to write temp file: " + file.getAbsolutePath(), e);
}
}? 部署前必做配置(CLI 命令):
heroku config:set HEROKU_TEMP_DIR="/tmp" # 或自定义路径(需确保可写,一般不建议) # heroku config:set HEROKU_TEMP_DIR="/app/tmp"
⚠️ 关键注意事项
- 不要依赖 File.createTempFile() 的默认行为:它可能仍落在只读目录下(取决于 JVM 配置),应显式指定 dir 参数;
- 临时文件需及时清理:Heroku 的 /tmp 在 dyno 重启后自动清空,但若逻辑中存在长期持有文件句柄或未删除的场景,建议在 finally 或 try-with-resources 后显式调用 file.delete();
- 避免大文件写入 /tmp:Heroku /tmp 有内存限制(通常约 1GB),超大会触发 OOM;大数据导出应改用流式响应(StreamingResponseBody)或对象存储(如 AWS S3);
- 日志中勿打印敏感路径:生产环境避免 System.out.println(file.getAbsolutePath()),改用 SLF4J 并设为 DEBUG 级别。
✅ 总结
Heroku 不是传统服务器——它没有“永久磁盘”,也没有固定项目结构。解决文件创建失败的核心原则是:放弃路径幻想,拥抱环境抽象。始终通过环境变量或已知安全路径(如 /tmp)获取可写目录,再动态构建文件对象。这一模式不仅适用于 JSON 临时文件,也适用于图片缓存、CSV 导出、PDF 生成等所有 I/O 场景。
立即学习“Java免费学习笔记(深入)”;


















