Java 向正在运行的Jar写入
我正在尝试创建一个Jar文件,该文件包含Jar中的数据,该数据在程序执行之间持续存在。我知道还有其他保存数据的方法,但我选择的是一个完全自包含的Jar文件 我有一些似乎有效的方法,我希望你们能对我的方法中的任何漏洞给予反馈,因为我觉得有些地方很粗糙。我现在做的是:Java 向正在运行的Jar写入,java,jar,Java,Jar,我正在尝试创建一个Jar文件,该文件包含Jar中的数据,该数据在程序执行之间持续存在。我知道还有其他保存数据的方法,但我选择的是一个完全自包含的Jar文件 我有一些似乎有效的方法,我希望你们能对我的方法中的任何漏洞给予反馈,因为我觉得有些地方很粗糙。我现在做的是: Jar包含一个文件saves.txt。此文件包含一位数字:开始时为1 当Jar运行时,它读取该文件(使用getResourceAsStream)并向用户显示该数字 然后,我使用ZipFile API将整个jar提取到一个临时目录中
- Jar包含一个文件saves.txt。此文件包含一位数字:开始时为1
- 当Jar运行时,它读取该文件(使用getResourceAsStream)并向用户显示该数字
- 然后,我使用ZipFile API将整个jar提取到一个临时目录中
- 既然saves.txt是一个文件(不是资源),我就增加这个数字并覆盖临时目录中的文件
- 然后我使用JarOutputStream将临时目录(包括更新的saves.txt文件)的内容重新压缩回同一个jar中
- 最后,我删除临时目录,程序退出。当用户再次运行jar时,saves.txt文件已经更新
- 这对签名JAR不起作用。这对我来说不是问题,因为这些罐子无论如何都不会签名
- 如果jar位于一个没有写权限的目录中,这将不起作用。我可以警告用户这一点
- 如果jar在运行程序时在存档资源管理器(如7zip)中打开,这将不起作用。我可以通过简单地向用户显示一条消息,要求他们关闭它来解决这个问题
- 这使得部署新版本的jar变得很烦人,因为保存在jar中。用户要么以某种方式从旧jar加载数据,要么开始清理。我也同意
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.swing.JOptionPane;
public class JarTest {
static List<File> extractedFiles = new ArrayList<File>();
public static void unzipWholeJar() throws IOException{
File jarFile = new File(JarTest.class.getProtectionDomain().getCodeSource().getLocation().getPath());
ZipFile jarZipFile = new ZipFile(jarFile);
Enumeration<? extends ZipEntry> entities = jarZipFile.entries();
while (entities.hasMoreElements()) {
ZipEntry entry = (ZipEntry)entities.nextElement();
if(!entry.isDirectory()){
InputStream in = jarZipFile.getInputStream(jarZipFile.getEntry(entry.getName()));
File file = new File("extracted/" + entry.getName());
extractedFiles.add(file);
File parent = new File(file.getParent());
parent.mkdirs();
OutputStream out = new FileOutputStream(file);
byte[] buffer = new byte[65536];
int bufferSize;
while ((bufferSize = in.read(buffer, 0, buffer.length)) != -1){
out.write(buffer, 0, bufferSize);
}
in.close();
out.close();
}
}
jarZipFile.close();
}
public static void rezipExtractedFiles() throws FileNotFoundException, IOException{
File jarFile = new File(JarTest.class.getProtectionDomain().getCodeSource().getLocation().getPath());
JarOutputStream jos = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(jarFile)));
for(File f : extractedFiles){
String absPath = f.getAbsolutePath().replaceAll("\\\\", "/");
String fileInJar = absPath.substring(absPath.indexOf("extracted") + 10);
addFile(jos, fileInJar, f);
}
jos.close();
}
public static void addFile(JarOutputStream jos, String fileInJar, File file) throws FileNotFoundException, IOException{
InputStream in = new FileInputStream(file);
jos.putNextEntry(new ZipEntry(fileInJar));
int bufferSize;
byte[] buffer = new byte[4096];
while ((bufferSize = in.read(buffer, 0, buffer.length)) != -1) {
jos.write(buffer, 0, bufferSize);
}
in.close();
jos.closeEntry();
}
public static void updateSavesFile(int newX) throws IOException{
BufferedWriter writer = new BufferedWriter(new FileWriter("extracted/saves.txt"));
writer.write(String.valueOf(newX));
writer.close();
}
public static void deleteExtracted(File f){
if (f.isDirectory()) {
for (File c : f.listFiles())
deleteExtracted(c);
}
f.delete();
}
public static void main(String... args) throws IOException{
//read saves file in
BufferedReader br = new BufferedReader(new InputStreamReader(JarTest.class.getClassLoader().getResourceAsStream("saves.txt")));
String line = br.readLine();
int x = Integer.parseInt(line);
br.close();
//show the message
JOptionPane.showMessageDialog(null, "Times opened: " + x);
//unzip the whole jar
//this will fail if the jar is in a folder without write privileges
//but I can catch that and warn the user
unzipWholeJar();
//update the unzipped saves file
updateSavesFile(x+1);
//put the files back into the jar
//this will fail if the jar is open in an archive explorer
//but I can catch that and warn the user
rezipExtractedFiles();
//delete what we extracted
deleteExtracted(new File("extracted"));
}
}
import java.io.BufferedOutputStream;
导入java.io.BufferedReader;
导入java.io.BufferedWriter;
导入java.io.File;
导入java.io.FileInputStream;
导入java.io.FileNotFoundException;
导入java.io.FileOutputStream;
导入java.io.FileWriter;
导入java.io.IOException;
导入java.io.InputStream;
导入java.io.InputStreamReader;
导入java.io.OutputStream;
导入java.util.ArrayList;
导入java.util.Enumeration;
导入java.util.List;
导入java.util.jar.JarOutputStream;
导入java.util.zip.ZipEntry;
导入java.util.zip.ZipFile;
导入javax.swing.JOptionPane;
公共类测试{
静态列表extractedFiles=new ArrayList();
public static void unzipWholeJar()引发IOException{
File jarFile=新文件(JarTest.class.getProtectionDomain().getCodeSource().getLocation().getPath());
ZipFile jarZipFile=新ZipFile(jarFile);
枚举概念
这有点可怕
- 与在jar之外使用配置文件相比,这是浪费
- 用户通常不关心配置文件的位置
- 它将状态保存在程序中。为什么这样不好?因为它们没有分开,用户无法在不复制整个程序的情况下轻松传输配置,或者在不共享其配置的情况下共享程序
- 如果可以读取和重写jar,还可以将配置文件写入同一目录
- 假设它确实可以完美地工作,它会使代码复杂化,可能是不必要的
- 如果它不起作用,你的用户可能会留下一个坏掉的可执行文件。这不是一个好的用户体验
您不需要将它放在同一个jar中。能够将jar的快照(与配置捆绑在一起)导出到程序中可能是一项功能,但在默认情况下,它可能没有足够的价值
实施:
当主机操作系统耗尽资源时会发生什么?文件描述符、空间。。。
您可能会在java.io.File.
中得到NullPointerException
,但是您在硬盘上留下了什么状态?您的程序可以处理现有文件吗?所有这些都是不必要的复杂性,您可以通过使用jar之外的文件来避免
由于外部原因,写入操作随时可能失败。而且由于您没有使用finally块来关闭流,我不确定更改是否会刷新
结论:
面对你在处理过程中遇到的持久性问题,这是一个聪明的技巧,但仍然是一个技巧。它会在某个时候为某人爆炸。如果你能避免,就去做。另一方面,我可能会误解这个问题
Map<String, String> zipProperties = new HashMap<>();
// zipProperties.put("create", "true");
zipProperties.put("encoding", "UTF-8");
try (FileSystem zipFS = FileSystems.newFileSystem(jarUri, zipProperties)) {
Path dataPath = zipFS.getPath("/data/file.txt");
...
Files.copy(inputStream, dataPath, StandardCopyOption.REPLACE_EXISTING);
}