【问题标题】:Can't Access Resources In Executable Jar无法访问可执行 Jar 中的资源
【发布时间】:2015-01-16 14:18:12
【问题描述】:

谁能指出我在这里做错了什么。

我有一个生成和发送 HTML 电子邮件的小型天气应用程序。使用下面的代码,当我从 Eclipse 运行它时,一切正常。我的电子邮件已生成,它能够访问我的图像资源并发送带有附件的电子邮件。

但是,当我通过运行 mvn install 构建可执行 jar 并使用 java -jar NameOfMyJar.jar 运行 jar 时,我得到 java.io.FileNotFound 我的图片资源例外。

我知道我访问图像资源的方式有问题,我只是不明白为什么它在未打包时可以正常工作,但每当我将其打包到 jar 中时就会爆炸。

非常感谢任何建议。


我的项目布局


我如何访问我的图片资源

//Setup the ATTACHMENTS
        MimeBodyPart attachmentsPart = new MimeBodyPart();
        try {
            attachmentsPart.attachFile("resources/Cloudy_Day.png");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }   

堆栈跟踪

    Exception in thread "main" java.lang.RuntimeException: javax.mail.MessagingException: IOException while sending message;
  nested exception is:
    java.io.FileNotFoundException: resources/Cloudy_Day.png (No such file or directory)
    at Utilities.SendEmailUsingGmailSMTP.SendTheEmail(SendEmailUsingGmailSMTP.java:139)
    at Utilities.SendEmailUsingGmailSMTP.SendWeatherEmail(SendEmailUsingGmailSMTP.java:66)
    at Weather.Main.start(Main.java:43)
    at Weather.Main.main(Main.java:23)
Caused by: javax.mail.MessagingException: IOException while sending message;
  nested exception is:
    java.io.FileNotFoundException: resources/Cloudy_Day.png (No such file or directory)
    at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:1167)
    at javax.mail.Transport.send0(Transport.java:195)
    at javax.mail.Transport.send(Transport.java:124)
    at Utilities.SendEmailUsingGmailSMTP.SendTheEmail(SendEmailUsingGmailSMTP.java:134)
    ... 3 more
Caused by: java.io.FileNotFoundException: resources/Cloudy_Day.png (No such file or directory)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.<init>(FileInputStream.java:146)
    at javax.activation.FileDataSource.getInputStream(FileDataSource.java:97)
    at javax.activation.DataHandler.writeTo(DataHandler.java:305)
    at javax.mail.internet.MimeBodyPart.writeTo(MimeBodyPart.java:1485)
    at javax.mail.internet.MimeBodyPart.writeTo(MimeBodyPart.java:865)
    at javax.mail.internet.MimeMultipart.writeTo(MimeMultipart.java:462)
    at com.sun.mail.handlers.multipart_mixed.writeTo(multipart_mixed.java:103)
    at javax.activation.ObjectDataContentHandler.writeTo(DataHandler.java:889)
    at javax.activation.DataHandler.writeTo(DataHandler.java:317)
    at javax.mail.internet.MimeBodyPart.writeTo(MimeBodyPart.java:1485)
    at javax.mail.internet.MimeMessage.writeTo(MimeMessage.java:1773)
    at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:1119)
    ... 6 more

【问题讨论】:

  • 尝试使用/resources/Cloudy_Day.png(路径前有/
  • 你当前的工作目录是什么?
  • 感谢大家的建议!当我今晚回家时,我会给他们一个机会,让你们知道情况如何。

标签: java maven jar jakarta-mail filenotfoundexception


【解决方案1】:

其他使用getResourceAsStream是正确的,但是路径有点棘手。您看到resources 文件夹中的小包图标了吗?这意味着resource 文件夹中的所有文件都将被放入类路径的根目录中。就像src/main/java 中的所有包都放在根目录中一样。所以你会从路径中取出resources

InputStream is = getClass().getResourceAsStream("/Cloudy_Day.png");

旁白: Maven 有一个文件结构约定。类路径资源通常放入src/main/resources。如果您在src/main 中创建resources 目录,Eclipse 应该会自动选择它,并为您应该在项目资源管理器中看到的路径src/main/resource 创建小包图标。这些文件也将进入根目录,并且可以以相同的方式访问。我会修复文件结构以遵循此约定。

注意:MimeBodyPart,可以是 ConstructedInputStream(正如 Bill Shannon 所建议的,这是不正确的)。正如他在下面的评论中提到的那样

“您也可以使用”附加数据

mbp.setDataHandler(new DataHandler(new ByteArrayDataSource(
          this.getClass().getResourceAsStream("/Cloudy_Day.png", "image/png"))));

【讨论】:

  • 注意:从流中构造 MimeBodyPart 并不像您认为的那样。它读取整个 MIME 部分 - 标题和数据 - 而不仅仅是附件中所需的数据。
  • @BillShannon Gotcha。感谢您的提醒。我会把它刮掉。我不熟悉这个 API。您建议如何使用 InputStream 处理 OP 用例?
  • @peeskillet 和 BillShannon 非常感谢你们!!!我修改了我的项目结构并使用 setDataHandler() 方法访问我的资源文件,现在一切正常。抱歉,我花了几天时间才回复你,但我最近忙得不可开交。无论如何,非常感谢你们的帮助。
【解决方案2】:

您不能将 JAR 文件中的资源作为文件访问,只能将它们作为 InputStream 读取:getResourceAsStream()

由于 MimeBodyPart 没有用于 InputStream 的 attach() 方法,因此最简单的方法应该是读取您的资源并将它们写入临时文件,然后附加这些文件。

【讨论】:

  • 您也可以使用 mbp.setDataHandler(new DataHandler(new ByteArrayDataSource(this.getClass().getResourceAsStream("/Cloudy_Day.png", "image/png")))) 附加数据
【解决方案3】:

试试这个

new MimeBodyPart().attachFile(new File(this.getClass().getClassLoader().getResource("resources/Cloudy_Day.png").toURI());

【讨论】:

    【解决方案4】:

    我不知道这是否会帮助任何人。但是,我有一个与 OP 类似的案例,我通过使用递归函数在类路径中查找文件解决了这个案例。这个想法是,当另一个开发人员决定将资源移动到另一个文件夹/路径时。只要名称不变,它仍然会被找到。

    比如在我的工作中,我们通常将资源放在jar之外,然后我们将所说的资源路径添加到我们的classpath中,所以这里资源的classpath会根据它所在的位置而有所不同。

    这就是我的代码起作用的地方,无论文件放在哪里,只要它在类路径中就会被找到。

    这是我的代码示例:

    import java.io.File;
    
    public class FindResourcesRecursive {
    
        public File findConfigFile(String paths, String configFilename) {
            for (String p : paths.split(File.pathSeparator)) {
                File result = findConfigFile(new File(p), configFilename);
                if (result != null) {
                    return result;
                }
            }
            return null;
        }
    
        private File findConfigFile(File path, String configFilename) {
            if (path.isDirectory()) {
                String[] subPaths = path.list();
                if (subPaths == null) {
                    return null;
                }
                for (String sp : subPaths) {
                    File subPath = new File(path.getAbsoluteFile() + "/" + sp);
                    File result = findConfigFile(subPath, configFilename);
                    if (result != null && result.getName().equalsIgnoreCase(configFilename)) {
                        return result;
                    }
                }
                return null;
            } else {
                File file = path;
                if (file.getName().equalsIgnoreCase(configFilename)) {
                    return file;
                }
                return null;
            }
        }
    
    }
    

    在这里,我有一个测试用例,它与我的 test/resources 文件夹中的文件“test.txt”相结合。该文件的内容是:

    A sample file
    

    现在,这是我的测试用例:

    import org.junit.Test;
    
    import java.io.*;
    
    import static org.junit.Assert.fail;
    
    public class FindResourcesRecursiveTest {
    
        @Test
        public void testFindFile() {
            // Here in the test resources I have a file "test.txt"
            // Inside it is a string "A sample file"
            // My Unit Test will use the class FindResourcesRecursive to find the file and print out the results.
            File testFile = new FindResourcesRecursive().findConfigFile(
                    System.getProperty("java.class.path"),
                    "test.txt"
            );
    
            try (FileInputStream is = new FileInputStream(testFile)) {
                int i;
                while ((i = is.read()) != -1) {
                    System.out.print((char) i);
                }
                System.out.println();
            } catch (IOException e) {
                fail();
            }
    
        }
    }
    

    现在,如果你运行这个测试,它会打印出“A sample file”并且测试会是绿色的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-02-16
      • 2016-02-15
      • 2012-11-28
      • 2011-02-08
      • 1970-01-01
      • 2011-10-20
      • 1970-01-01
      • 2015-08-03
      相关资源
      最近更新 更多