如何在Jar文件内或其旁执行脚本并处理相关文件?
问题解决思路与方案
核心问题说明
打包成Jar后,/src/main/resources/这类路径完全失效——Jar是压缩文件,内部资源无法直接作为文件系统路径被ProcessBuilder调用,而且Jar内的文件只读,根本无法在Jar内部创建或修改令牌、文本、JSON这类文件。
最佳方案:分离可执行脚本与可修改文件到外部目录
结合你的服务器目录结构(/Deployments/Data),把所有需要执行、修改的文件都放到Data目录下,具体步骤:
1. 提取Jar内的脚本到外部Data目录
启动程序时,先把Jar中打包的hello.bat脚本复制到/Deployments/Data目录(确保该目录有读写权限),再执行这个外部的脚本文件:
// 定义外部Data目录路径,建议通过系统参数或配置文件传入,避免硬编码 private final String EXTERNAL_DATA_DIR = "/Deployments/Data/"; private final String SCRIPT_NAME = "hello.bat"; public void runScript4() { // 先将Jar内的脚本提取到外部目录 extractResourceToExternalDir(SCRIPT_NAME, EXTERNAL_DATA_DIR); String scriptPath = EXTERNAL_DATA_DIR + SCRIPT_NAME; try { Process process = new ProcessBuilder(scriptPath).start(); process.waitFor(); System.out.println("Script executing..."); BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream())); String inputLine; StringBuilder result = new StringBuilder(); // 用StringBuilder替代String拼接,提升效率 while ((inputLine = in.readLine()) != null) { System.out.println(inputLine); result.append(inputLine); } in.close(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } } // 提取Jar内资源到外部目录的工具方法 private void extractResourceToExternalDir(String resourceName, String targetDir) { File targetFile = new File(targetDir + resourceName); // 若文件已存在,跳过提取(避免每次启动重复覆盖) if (targetFile.exists()) { return; } // 创建目标目录(如果不存在) new File(targetDir).mkdirs(); // 从Jar读取资源并写入外部文件 try (InputStream resourceStream = getClass().getResourceAsStream("/" + resourceName); FileOutputStream outputStream = new FileOutputStream(targetFile)) { if (resourceStream == null) { throw new IOException("Resource " + resourceName + " not found in Jar"); } byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = resourceStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } // 若为Linux系统,需给脚本添加可执行权限 targetFile.setExecutable(true); } catch (IOException e) { e.printStackTrace(); } }
2. 所有可修改文件(令牌、txt、JSON)都放在Data目录
- 本地开发时,可在项目根目录创建
Data文件夹,将txt/JSON文件放入其中,再配置IDE把Data设为资源目录,让本地运行时直接读取该目录,和线上环境保持一致。 - 线上部署时,将初始的txt/JSON文件放到
/Deployments/Data目录,程序直接读写该目录下的文件,示例代码:
// 读取Data目录下的文本文件 public String readTxtFile() throws IOException { File txtFile = new File(EXTERNAL_DATA_DIR + "data.txt"); return Files.readString(txtFile.toPath()); } // 修改Data目录下的JSON文件 public void updateJsonFile(String newContent) throws IOException { File jsonFile = new File(EXTERNAL_DATA_DIR + "config.json"); Files.writeString(jsonFile.toPath(), newContent); }
3. 避免硬编码路径的小技巧
不要直接写死/Deployments/Data,可通过JVM启动参数传入,比如:
java -jar project.jar -Ddata.dir=/Deployments/Data
然后在代码中读取:
private final String EXTERNAL_DATA_DIR = System.getProperty("data.dir", "./Data/");
这样本地和线上的路径可灵活配置,无需修改代码。
关于Jar内修改文件的疑问
绝对不建议尝试在Jar内创建或修改文件:
- Jar运行时会被JVM加载,多数情况下文件处于锁定状态,无法修改。
- 即使成功修改,下次重新部署Jar时,所有修改内容会被覆盖。
- 若Jar带有数字签名,修改后签名会失效,导致Jar无法被JVM加载。
内容的提问来源于stack exchange,提问作者Jane Foster
相关产品推荐
相关产品推荐

