1

I'm running spring boot application with a jar file.

This application sends a mail alond with attachment. The attachment is the part of the jar file. I'm using the code below to fetch the attachment. I have referred this link Classpath resource not found when running as jar

    public boolean sendEmail(String content, String subject, String from, String to, String cc, boolean isAttach, List<String> attachFiles, Session session) {

            try {

                Message message = new MimeMessage(session);
                message.setFrom(new InternetAddress(from, "XXX"));
                message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
                if(cc!=null && !cc.equals("")) {
                    message.setRecipients(Message.RecipientType.CC, InternetAddress.parse(cc));
                }

                message.setSubject(subject);

                MimeBodyPart messageBodyPart = new MimeBodyPart();
                messageBodyPart.setContent(content, "text/html; charset=utf8");

                // creates multipart
                Multipart multipart = new MimeMultipart();
                multipart.addBodyPart(messageBodyPart);

                if (isAttach) {
                    // adds attachments
                    if (!attachFiles.isEmpty()) {
                        for (String filePath : attachFiles) {
                            try {

                                ClassPathResource classPathResource = new ClassPathResource("Brochure.pdf");
                                InputStream inputStream = classPathResource.getInputStream();

                                MimeBodyPart attachPart = new MimeBodyPart();

                                attachPart.attachFile(IOUtils.toString(inputStream));

                                multipart.addBodyPart(attachPart);
                            } catch (IOException ex) {
                                ex.printStackTrace();
                            }
                        }
                    }
                }

                // sets the multipart as email's content
                message.setContent(multipart);
                Transport.send(message);
                System.out.println("sent email for " + to);
                return true;
            } catch (MessagingException | UnsupportedEncodingException e) {
                System.out.println("email sending failed for " + to);
                e.printStackTrace();
                // throw new RuntimeException(e);
                return false;
            }
        }

I'm using the getInputStream() function itself in order to search for the file inside the jar file. But I'm getting the following error:

    javax.mail.MessagingException: IOException while sending message;
      nested exception is:
        java.io.FileNotFoundException: Invalid file path
        at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:1365)
        at javax.mail.Transport.send0(Transport.java:255)
        at javax.mail.Transport.send(Transport.java:124)
        at com.inversation.app.integration.service.mail.clients.GenericMailClient.sendEmail(GenericMailClient.java:95)
        at com.inversation.app.jobs.CronJob.executeInternal(CronJob.java:62)
        at org.springframework.scheduling.quartz.QuartzJobBean.execute(QuartzJobBean.java:75)
        at org.quartz.core.JobRunShell.run(JobRunShell.java:202)
        at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)
    Caused by: java.io.FileNotFoundException: Invalid file path
        at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
        at javax.activation.FileDataSource.getInputStream(FileDataSource.java:110)
        at javax.activation.DataHandler.writeTo(DataHandler.java:318)
        at javax.mail.internet.MimeBodyPart.writeTo(MimeBodyPart.java:1694)
        at javax.mail.internet.MimeBodyPart.writeTo(MimeBodyPart.java:996)
        at javax.mail.internet.MimeMultipart.writeTo(MimeMultipart.java:561)
        at com.sun.mail.handlers.multipart_mixed.writeTo(multipart_mixed.java:84)
        at javax.activation.ObjectDataContentHandler.writeTo(DataHandler.java:901)
        at javax.activation.DataHandler.writeTo(DataHandler.java:330)
        at javax.mail.internet.MimeBodyPart.writeTo(MimeBodyPart.java:1694)
        at javax.mail.internet.MimeMessage.writeTo(MimeMessage.java:1913)
        at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:1315)
        ... 7 more

Before podting this question here i have done research my myside, but still not able to solve the problem. Any help would be appreciated.

I'm able to solve the issue by using below code. Hope it helps others. Refer this issue - Similar issue

attachPart.setDataHandler(new DataHandler(new ByteArrayDataSource(this.getClass().getResourceAsStream("/"+filePath),"application/pdf")));
coders
  • 719
  • 1
  • 11
  • 35
  • A resource in your classpath, especially inside a jar isn't a file. So using `attachFile(String)` will not work. Next to that this method is used to append a file with a name not with the file content as a string. If you solved it, as your answer in the question explains, then write an answer.. – M. Deinum Mar 19 '19 at 11:57

3 Answers3

1

(Caution: You are hard-coding "Brochure.pdf", and ignoring filePath entirely in your loop. I shall assume you meant to attach each value of filePath.)

As you can see in the documentation for the attachFile method, the String argument must be a valid file name.

Converting the bytes in an InputStream to a String certainly will not result in a file name.

A resource in a .jar is not a file. It’s part of a .jar file, not a separate file.

You will need to set the MimeBodyPart’s content and file name manually, instead of using attachFile:

URL content = GenericMailClient.class.getResource("/" + filePath);
attachPart.setDataHandler(new DataHandler(content));

URI uri = content.toURI();
String path = uri.getPath();
String fileName = path.substring(path.lastIndexOf('/') + 1);

attachPart.setDisposition(Part.ATTACHMENT);
attachPath.setFileName(fileName);

(Do not attempt to use the getFile() method of URL. The getFile() method will not return a valid file name, both because the URL won’t be a file: URL, and because characters which are not allowed to appear directly in URLs (like spaces) will be percent-escaped. On the other hand, the URI class properly parses URI components and returns their unescaped forms.)

VGR
  • 40,506
  • 4
  • 48
  • 63
-1

I Guess, You need to change the way of reading file using springs ResourceUtils from class path in spring boot application as:

File file = ResourceUtils.getFile("classpath:Brochure.pdf");
InputStream in = new FileInputStream(file);

I am sure, it will work for you

San
  • 66
  • 1
  • 8
  • This doesn't work in the case of jar – coders Mar 19 '19 at 10:13
  • @coders Do you have build jar using maven? or like export jar as runnable? I think, Maven is the best way to build jar if I am not wrong. – San Mar 19 '19 at 10:18
  • Using Maven itself. – coders Mar 19 '19 at 10:19
  • @coders Okay, I hope [Stackoverflow](https://stackoverflow.com/questions/25869428/classpath-resource-not-found-when-running-as-jar) this one will help you which is relate to the same issue. – San Mar 19 '19 at 10:23
-1

I have used this below code in my projects. Basically, what I do is scan the whole classpath for the file intended if I don't have the exact path. This is to help me in making sure that I don't need to have the file in a specific path.

This is because I encountered the same error as you when I use this line.

getClass().getClassLoader().getResource("Brochure.pdf")

So, I created two functions that takes a filename, and the classpaths included in the jar and recursively searched for the file.

  private static 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 static 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;
    }
  }

Here is an example of my code in action:

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;
        }
    }

}

Here I have a test case that is coupled with a file "test.txt" in my test/resources folder. The content of said file is:

A sample file

Now, here is my test case:

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();
        }

    }
}

Now, if you run this test, it will print out "A sample file" and the test will be green.

Now, is this okay with you?