![](/img/trans.png)
[英]Java - Change the name of email attachment using javax.mail library
[英]Send email with javax.mail using an existing InputStream as attachment content
是否可以使用javax.mail
并使用“现有” InputStream
作为电子邮件附件内容发送电子邮件?
目前我正在构建电子邮件消息如下:
final MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));
message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
message.setSubject("Subject line");
final Multipart multipartContent = new MimeMultipart();
final MimeBodyPart textPart = new MimeBodyPart();
textPart.setText("Message body");
multipartContent.addBodyPart(textPart);
final MimeBodyPart attachmentPart = new MimeBodyPart();
final DataSource source = new InputStreamDataSource("text/plain", "test.txt", new ByteArrayInputStream("CONTENT INPUT STREAM".getBytes()));
attachmentPart.setDataHandler(new DataHandler(source));
attachmentPart.setFileName("text.txt");
multipartContent.addBodyPart(attachmentPart);
message.setContent(multipartContent);
InputStreamDataSource
实现如下:
public class InputStreamDataSource implements DataSource
{
private final String contentType;
private final String name;
private final InputStream inputStream;
public InputStreamDataSource(String contentType, String name, InputStream inputStream)
{
this.contentType = contentType;
this.name = name;
this.inputStream = inputStream;
}
public String getContentType()
{
return contentType;
}
public String getName()
{
return name;
}
public InputStream getInputStream() throws IOException
{
System.out.println("CALLED TWICE: InputStreamDataSource.getInputStream()");
return new BufferedInputStream(inputStream);
//return new ByteArrayInputStream("THIS 'NEW' INPUT STREAM WORKS BUT 'EXISTING' INPUT STREAM RESULTS IN ZERO-BYTE ATTACHMENT".getBytes());
}
public OutputStream getOutputStream() throws IOException
{
throw new UnsupportedOperationException("Not implemented");
}
}
DataSource
提供方法getInputStream()
以获取电子邮件附件内容的InputStream
。
如果我返回一个“新” InputStream
不依赖于“现有” InputStream
然后正常工作。 但是如果我返回一个“现有的” InputStream ,那么电子邮件消息将与一个零字节附件一起发送。
是否可以使用javax.mail
发送电子邮件,并使用“现有” InputStream
作为电子邮件附件内容?
编辑:
见https://community.oracle.com/thread/1590625
TL;DR 使用ByteArrayDataSource
必须深入研究 Oracle 的源代码... https://java.net/projects/javamail/sources/mercurial/content/mail/src/main/java/javax/mail/internet/MimeBodyPart.java
当前的 java 邮件实现在输入流上运行了 2 次:
...哪种情况很糟糕,因为整个流(在慢速连接上可能有数百 MB)将被读取两次 ...并且对于一旦读取就“消耗”的流会导致这个问题。
我尝试的第一个“解决方法”是自己指定标题:
attachmentPart.setDataHandler(new DataHandler(source));
attachmentPart.setHeader("Content-Transfer-Encoding", "8bit");
attachmentPart.setHeader("Content-Type", ds.getContentType() + "; " + ds.getName());
...按照这个顺序,而不是相反...因为出于某种原因setDataHandler
内部调用另一个方法invalidateContentHeaders
再次清除"Content-Transfer-Encoding"
标头(wtf?!)
听起来不错,邮件已发送,万岁!!! :D ... :( 见下
附件发送...但坏了
我的邮件服务器中收到的文件已损坏。 呵呵。 为什么?! . 在这个蹩脚的 java 邮件代码中进行了长时间的搜索和研究之后,我找到了它,它们将InputStream
管道化为LineOutputStream
,它改变了二进制数据的行尾。 嗯。 java邮件实现真的是一团糟。 :/
我重写了你的 InputStreamDataSource 类,它对我有用。
class InputStreamDataSource implements DataSource {
String contentType;
String name;
byte[] fileData;
public InputStreamDataSource(String contentType, String name, InputStream inputStream) throws IOException {
this.contentType = contentType;
this.name = name;
/**
* It seems DataSource will close inputStream and reopen it.
* I converted inputStream to a byte array, so it won't be closed again.
*/
fileData = IOUtils.toByteArray(inputStream);
}
public String getContentType() {
return contentType;
}
public String getName() {
return name;
}
public InputStream getInputStream() throws IOException {
/**
* Convert byte array back to inputStream.
*/
return new ByteArrayInputStream(fileData);
}
public OutputStream getOutputStream() throws IOException {
throw new UnsupportedOperationException("Not implemented");
}
}
如果InputStream
包含 mime 标头,则使用javax.mail.internet.MimeBodyPart(InputStream)
构造函数。 您不需要使用自定义DataSource
类。
否则,如果InputStream
只是没有标题的正文,则将流转换为字节数组并使用javax.mail.internet.MimeBodyPart(InternetHeaders, byte[])
构造函数提供标题。
我解决了将InputStream
转换为字节数组并将其转换为 Base64 格式的问题。
//get file name
String fileName = ...;
//get content type
String fileContentType = ...;
//get file content
InputStream fileStream = ...;
//convert to byte array
byte[] fileByteArray = IOUtils.toByteArray(fileStream);
//and convert to Base64
byte[] fileBase64ByteArray = java.util.Base64.getEncoder().encode(fileByteArray);
//manually define headers
InternetHeaders fileHeaders = new InternetHeaders();
fileHeaders.setHeader("Content-Type", fileContentType + "; name=\"" + fileName + "\"");
fileHeaders.setHeader("Content-Transfer-Encoding", "base64");
fileHeaders.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
//build MIME body part
MimeBodyPart mbp = new MimeBodyPart(fileHeaders, fileBase64ByteArray);
mbp.setFileName(fileName);
//add it to the multipart
multipart.addBodyPart(mbp);
我使用此代码发送带有网络下载附件的电子邮件。 您可以根据自己的目的轻松编辑它。 在 mimeType 中使用附件的 mime 类型。 快乐编码。
try {
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(
"sender@gmail.com"));
message.setRecipients(Message.RecipientType.TO,
InternetAddress.parse("reciever@gmail.com"));
message.setSubject("subject");
Multipart multipart = new MimeMultipart();
URL url = new URL(url);
InputStream is = url.openStream();
MimeBodyPart bodyPart = new MimeBodyPart(is);
multipart.addBodyPart(bodyPart);
message.setContent(multipart);
message.addHeader("Content-Type", mimeType);
Transport.send(message);
logger.info("SENT to" + message.getRecipients(RecipientType.TO));
} catch (MessagingException e) {
//some implementation
}
当前的 java 邮件实现两次遍历输入流:第一遍检测数据的编码,第二遍发送数据。
如果您使用 EncodingAware 接口指定编码,则可以防止第一次通过。 提供的 DataSource 应该实现这个接口。 下面是一个例子:
public class AttachementDataSource implements javax.activation.DataSource, javax.mail.EncodingAware {
private final InputStreamSource inputStreamSource;
public AttachementDataSource(InputStreamSource inputStreamSource) {
this.inputStreamSource = inputStreamSource;
}
@Override
public InputStream getInputStream() throws IOException {
return inputStreamSource.getInputStream();
}
@Override
public OutputStream getOutputStream() throws IOException {
throw new UnsupportedOperationException("Read-only javax.activation.DataSource");
}
@Override
public String getContentType() {
return "application/octet-stream";
}
@Override
public String getName() {
return "inline";
}
@Override
public String getEncoding() {
return "base64";
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.