在JAR文件中运行被打包的EXE文件

31

我正在通过Java程序执行一个exe文件。路径在Java代码中被硬编码。

我已经将exe文件打包在jar包中。

但是由于路径名在Java文件中被硬编码,所以我无法将我的jar包作为独立程序运行。

有什么提示可以把exe文件打包到jar包中并能够将其作为独立程序运行吗?

6个回答

37

这将从远程服务器获取.exe文件,并将其提取到本地磁盘上的一个文件中。当Java程序退出时,该文件将被删除。

import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

public class Main
{
    public static void main(final String[] args)
        throws URISyntaxException,
               ZipException,
               IOException
    {
        final URI uri;
        final URI exe;

        uri = getJarURI();
        exe = getFile(uri, "Main.class");
        System.out.println(exe);
    }

    private static URI getJarURI()
        throws URISyntaxException
    {
        final ProtectionDomain domain;
        final CodeSource       source;
        final URL              url;
        final URI              uri;

        domain = Main.class.getProtectionDomain();
        source = domain.getCodeSource();
        url    = source.getLocation();
        uri    = url.toURI();

        return (uri);
    }

    private static URI getFile(final URI    where,
                               final String fileName)
        throws ZipException,
               IOException
    {
        final File location;
        final URI  fileURI;

        location = new File(where);

        // not in a JAR, just return the path on disk
        if(location.isDirectory())
        {
            fileURI = URI.create(where.toString() + fileName);
        }
        else
        {
            final ZipFile zipFile;

            zipFile = new ZipFile(location);

            try
            {
                fileURI = extract(zipFile, fileName);
            }
            finally
            {
                zipFile.close();
            }
        }

        return (fileURI);
    }

    private static URI extract(final ZipFile zipFile,
                               final String  fileName)
        throws IOException
    {
        final File         tempFile;
        final ZipEntry     entry;
        final InputStream  zipStream;
        OutputStream       fileStream;

        tempFile = File.createTempFile(fileName, Long.toString(System.currentTimeMillis()));
        tempFile.deleteOnExit();
        entry    = zipFile.getEntry(fileName);

        if(entry == null)
        {
            throw new FileNotFoundException("cannot find file: " + fileName + " in archive: " + zipFile.getName());
        }

        zipStream  = zipFile.getInputStream(entry);
        fileStream = null;

        try
        {
            final byte[] buf;
            int          i;

            fileStream = new FileOutputStream(tempFile);
            buf        = new byte[1024];
            i          = 0;

            while((i = zipStream.read(buf)) != -1)
            {
                fileStream.write(buf, 0, i);
            }
        }
        finally
        {
            close(zipStream);
            close(fileStream);
        }

        return (tempFile.toURI());
    }

    private static void close(final Closeable stream)
    {
        if(stream != null)
        {
            try
            {
                stream.close();
            }
            catch(final IOException ex)
            {
                ex.printStackTrace();
            }
        }
    }
}

7
别忘了设置可执行标志,否则你的代码只能在Windows上运行。 - akuhn
2
在创建临时文件并在退出时标记其删除方面,+1。 - gencoreoperative
我知道这是一个老问题,但我正在尝试在Windows 7上使用类似于此的代码,但我无法在临时目录中执行.exe文件。如果您可以查看我的问题:https://dev59.com/EHbZa4cB1Zd3GeqPE1on,我将不胜感激。 - Andy
@gencoreoperative 实际上,这是一个棘手的问题,如果在错误的上下文中完成,很容易造成内存泄漏。 - Markus Malkusch
请记住,使用这种方法很可能会导致您的应用程序被杀毒软件标记。 - m4heshd

12
操作系统并不关心或知道.jar文件,因此在执行前,您需要将.exe文件解压缩到某个临时位置。

我使用D.Snap描述的方法,将文件从资源复制到项目的根目录,并现在像这样引用该文件: "File file = new File("your.exe");"。 因此,您可以像这样打开文件以写入来自资源目录的根目录(如果您正在使用maven项目): "InputStream is = getClass().getResource("your.exe").openStream();" - hipokito

6
//gets program.exe from inside the JAR file as an input stream
InputStream is = getClass().getResource("program.exe").openStream();
//sets the output stream to a system folder
OutputStream os = new FileOutputStream("program.exe");

//2048 here is just my preference
byte[] b = new byte[2048];
int length;

while ((length = is.read(b)) != -1) {
    os.write(b, 0, length);
}

is.close();
os.close();

请描述您的代码。什么是FileOutputStream?我进行了测试,但不起作用。 - ParisaN

3
虽然其他用户已经正确回答了这个问题,提取并运行然后清理。另一个要考虑的问题是完全采用本地方式。
您已经使用本机二进制文件来完成特定任务。为什么不创建本地安装程序,安装您的应用程序,并将二进制文件安装到操作系统特定位置(Win32上的Program Files),并创建合适的快捷方式。
这样,您的应用程序将感觉更原生化,这意味着您不需要编写或管理代码来解决此问题。目前的Jar看起来像是跨平台的代码(Jar可以在任何地方运行对吧?),但打包了一个本地二进制文件,这些文件无法在所有地方运行。这感觉像是一种矛盾。
对于安装程序,我可以推荐Nullsoft Installation System(NSIS),因为他们有许多优秀的教程和代码示例供学习。

2

使用

getClass().getResource(what).openStream()

并将其复制到磁盘中的另一个文件。

-5
您可以编写一个简单的Java程序,使用Runtime.exec()来启动exe。然后,您可以将jar文件的"Main-Class"属性设置为该启动器类。用户随后可以运行您的jar文件,以便运行exe文件。

在这种情况下,我需要在 Java 代码中硬编码 exe 路径。我想让它可移植。 - krisp
@Krisp。不是真的。您可以使用Class/ClassLoader.getResource()方法在JAR文件中查找相对路径。 - Kevin
谢谢,这看起来有一些希望,我会尝试一下并让您知道。 - krisp
1
这是行不通的 - Runtime.exec() 告诉操作系统去执行某个东西,而操作系统无法在 JAR 文件内部执行任何东西。 - Michael Borgwardt
@Michael 可以将可执行文件提取到临时目录并从那里运行。 - Kevin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接