diff --git a/spring-context-support/src/main/java/org/springframework/mail/javamail/MimeMessageHelper.java b/spring-context-support/src/main/java/org/springframework/mail/javamail/MimeMessageHelper.java index 84400943ced..11495a15ad5 100644 --- a/spring-context-support/src/main/java/org/springframework/mail/javamail/MimeMessageHelper.java +++ b/spring-context-support/src/main/java/org/springframework/mail/javamail/MimeMessageHelper.java @@ -905,12 +905,47 @@ public class MimeMessageHelper { * @see #addInline(String, org.springframework.core.io.Resource) */ public void addInline(String contentId, DataSource dataSource) throws MessagingException { + addInline(contentId, null, dataSource); + } + + /** + * Add an inline element to the MimeMessage, taking the content from a + * {@code jakarta.activation.DataSource} and assigning the provided + * {@code inlineFileName} to the element. + *

Note that the InputStream returned by the DataSource implementation + * needs to be a fresh one on each call, as JavaMail will invoke + * {@code getInputStream()} multiple times. + *

NOTE: Invoke {@code addInline} after {@link #setText}; + * else, mail readers might not be able to resolve inline references correctly. + * @param contentId the content ID to use. Will end up as "Content-ID" header + * in the body part, surrounded by angle brackets: e.g. "myId" → "<myId>". + * Can be referenced in HTML source via src="cid:myId" expressions. + * @param inlineFilename the fileName to use for the inline element's part + * @param dataSource the {@code jakarta.activation.DataSource} to take + * the content from, determining the InputStream and the content type + * @throws MessagingException in case of errors + * @since 6.2 + * @see #addInline(String, java.io.File) + * @see #addInline(String, org.springframework.core.io.Resource) + */ + public void addInline(String contentId, @Nullable String inlineFilename, DataSource dataSource) + throws MessagingException { + Assert.notNull(contentId, "Content ID must not be null"); Assert.notNull(dataSource, "DataSource must not be null"); MimeBodyPart mimeBodyPart = new MimeBodyPart(); mimeBodyPart.setDisposition(Part.INLINE); mimeBodyPart.setContentID("<" + contentId + ">"); mimeBodyPart.setDataHandler(new DataHandler(dataSource)); + if (inlineFilename != null) { + try { + mimeBodyPart.setFileName(isEncodeFilenames() ? + MimeUtility.encodeText(inlineFilename) : inlineFilename); + } + catch (UnsupportedEncodingException ex) { + throw new MessagingException("Failed to encode inline filename", ex); + } + } getMimeMultipart().addBodyPart(mimeBodyPart); } @@ -989,14 +1024,75 @@ public class MimeMessageHelper { public void addInline(String contentId, InputStreamSource inputStreamSource, String contentType) throws MessagingException { + addInline(contentId, "inline", inputStreamSource, contentType); + } + + /** + * Add an inline element to the MimeMessage, taking the content from an + * {@code org.springframework.core.InputStreamResource}, and + * specifying the inline fileName explicitly. + *

The content type will be determined by the name of the given + * content file. Do not use this for temporary files with arbitrary + * filenames (possibly ending in ".tmp" or the like)! + *

Note that the InputStream returned by the InputStreamSource implementation + * needs to be a fresh one on each call, as JavaMail will invoke + * {@code getInputStream()} multiple times. + *

NOTE: Invoke {@code addInline} after {@code setText}; + * else, mail readers might not be able to resolve inline references correctly. + * @param contentId the content ID to use. Will end up as "Content-ID" header + * in the body part, surrounded by angle brackets: e.g. "myId" → "<myId>". + * Can be referenced in HTML source via src="cid:myId" expressions. + * @param inlineFilename the file name to use for the inline element + * @param inputStreamSource the resource to take the content from + * @throws MessagingException in case of errors + * @since 6.2 + * @see #setText(String) + * @see #getFileTypeMap + * @see #addInline(String, org.springframework.core.io.Resource) + * @see #addInline(String, String, jakarta.activation.DataSource) + */ + public void addInline(String contentId, String inlineFilename, InputStreamSource inputStreamSource) + throws MessagingException { + + String contentType = getFileTypeMap().getContentType(inlineFilename); + addInline(contentId, inlineFilename, inputStreamSource, contentType); + } + + /** + * Add an inline element to the MimeMessage, taking the content from an + * {@code org.springframework.core.InputStreamResource}, and + * specifying the inline fileName and content type explicitly. + *

You can determine the content type for any given filename via a Java + * Activation Framework's FileTypeMap, for example the one held by this helper. + *

Note that the InputStream returned by the InputStreamSource implementation + * needs to be a fresh one on each call, as JavaMail will invoke + * {@code getInputStream()} multiple times. + *

NOTE: Invoke {@code addInline} after {@code setText}; + * else, mail readers might not be able to resolve inline references correctly. + * @param contentId the content ID to use. Will end up as "Content-ID" header + * in the body part, surrounded by angle brackets: e.g. "myId" → "<myId>". + * Can be referenced in HTML source via src="cid:myId" expressions. + * @param inlineFilename the fileName to use for the inline element's part + * @param inputStreamSource the resource to take the content from + * @param contentType the content type to use for the element + * @throws MessagingException in case of errors + * @since 6.2 + * @see #setText + * @see #getFileTypeMap + * @see #addInline(String, org.springframework.core.io.Resource) + * @see #addInline(String, String, jakarta.activation.DataSource) + */ + public void addInline(String contentId, String inlineFilename, InputStreamSource inputStreamSource, String contentType) + throws MessagingException { + Assert.notNull(inputStreamSource, "InputStreamSource must not be null"); if (inputStreamSource instanceof Resource resource && resource.isOpen()) { throw new IllegalArgumentException( "Passed-in Resource contains an open stream: invalid argument. " + "JavaMail requires an InputStreamSource that creates a fresh stream for every call."); } - DataSource dataSource = createDataSource(inputStreamSource, contentType, "inline"); - addInline(contentId, dataSource); + DataSource dataSource = createDataSource(inputStreamSource, contentType, inlineFilename); + addInline(contentId, inlineFilename, dataSource); } /**