JavaMail发送字符串邮件附件-编码UTF-8

30
我的应用程序需要发送一个文本文件,需要先将它生成为一个字符串。文本包含非ASCII符号,所以我希望使用UTF-8编码。我尝试了很多变量,但是所有做法都只能得到一些问号作为附件。当我将同样的文本作为消息正文发送时,它可以正常工作。 这是生成带有附件的MimeBodyPart的代码行:
String attachment = "Привет";
messageBodyPart.setContent(new String(attachment.getBytes("UTF-8"),
    "UTF-8"),"text/plain; charset=UTF-8");
我也尝试过不进行任何转换,仅使用字节串,现在正试图从字节串生成字符串...我做错了什么?(我记得在另一个项目中这样做是可以的,但我现在无法访问它的源代码)。
提前感谢。 Timofey.
更新
阅读了你们的回复后,再进行了一些不成功的实验后,我认为最好公布我的邮件发送代码。 我有一个Mailer类,它负责发送邮件,其他类只需调用其静态sendMessage()方法即可发送消息。而且它全部在Google App Engine上运行。
public static void sendMessage(String to, String subject, String msgBody,
            String attachment) throws AddressException, MessagingException {

    Properties props = new Properties();

    Session mailSession = Session.getDefaultInstance(props, null);
    Message msg = new MimeMessage(mailSession);
    String email = "bla-bla-bla"; // userService.getCurrentUser().getEmail();

    msg.setFrom(new InternetAddress(email));
    msg.addRecipient(Message.RecipientType.TO, new InternetAddress(to));

    InternetAddress[] addresses = { new InternetAddress("bla-bla-bla") };

    msg.setReplyTo(addresses);
    msg.setSubject(subject);

    Calendar cal = Calendar.getInstance();

    String fileName = cal.get(Calendar.YEAR) + "_"
            + cal.get(Calendar.MONTH) + "_"
            + cal.get(Calendar.DAY_OF_MONTH) + "_"
            + cal.get(Calendar.HOUR_OF_DAY) + "_"
            + cal.get(Calendar.MINUTE) + "_" + cal.get(Calendar.SECOND)
            + "_" + cal.get(Calendar.MILLISECOND) + ".txt";

    // create the message part
    MimeBodyPart messageBodyPart = new MimeBodyPart();

    // fill message
    // Here we should have the msgBody.
    // Sending attachment contents for debugging only.
    messageBodyPart.setText(attachment + " - 4", "UTF-8", "plain");

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

    MimeBodyPart att = new MimeBodyPart();
    att.setText(attachment, "UTF-8", "plain");
    att.addHeader("Content-Type", "text/plain; charset=UTF-8"); 

    att.setFileName(fileName);
    multipart.addBodyPart(att);

    // Put parts in message
    msg.setContent(multipart);

    Transport.send(msg);
}

并且在另一个类中调用这个东西的行是:

Mailer.sendMessage("mymail@example.com", "Test", "No body", "Привет, Я кусок текста");

而邮件的原始源代码,奇怪的是(省略了看似无关紧要的标头):

Message-ID: <00163662e7107ccbe3049c1402fb@google.com>
Date: Sat, 12 Feb 2011 11:21:01 +0000
Subject: Pages
From: mymail@example.com
To: mymail@example.com
Content-Type: multipart/mixed; boundary=00163662e7107ccbd4049c1402fa

--00163662e7107ccbd4049c1402fa
Content-Type: text/plain; charset=KOI8-R; format=flowed; delsp=yes
Content-Transfer-Encoding: base64

8NLJ18XULCDxIMvV08/LINTFy9PUwSAtIDQNCg==
--00163662e7107ccbd4049c1402fa
Content-Type: text/plain; charset=US-ASCII; name="2011_1_12_11_21_1_691.txt"
Content-Disposition: attachment; filename="2011_1_12_11_21_1_691.txt"
Content-Transfer-Encoding: base64

Pz8/Pz8/LCA/ID8/Pz8/ID8/Pz8/Pw==
--00163662e7107ccbd4049c1402fa--
我就是不明白,为什么字符集与我尝试设置的不同,它们从哪里来的。

你能在你生成的文件中看到正确的字符编码吗? - JSS
在生成的文件中,我只看到问号。 - Ibolit
8个回答

25

将内容类型设置为application/octet-stream

MimeBodyPart attachmentPart = new MimeBodyPart();

try {
  DataSource ds = new ByteArrayDataSource(attachment.getBytes("UTF-8"), "application/octet-stream");
  attachmentPart = new MimeBodyPart();
  attachmentPart.setDataHandler(new DataHandler(ds));
} 
catch (Exception e) {
  Logger.getLogger("Blina").log(Level.SEVERE, Misc.getStackTrace(e));
}

attachmentPart.setFileName(fileName);
multipart.addBodyPart(attachmentPart);

// Put parts in message
msg.setContent(multipart);

8

我遇到过类似的情况,下面的代码可以解决这个问题:

MimeBodyPart att = new MimeBodyPart();
att.setFileName(MimeUtility.encodeText(fileName));

5
如果问题出现在文件名而不是正文中,以下代码可以解决问题(适用于希伯来语):
MimeBodyPart attachment = new MimeBodyPart();
attachment.setFileName(MimeUtility.encodeText(filename, "UTF-8", null));

1
在我的情况下,附件名称是使用日语字符集的,我尝试了上面的代码行,但是我在收到的电子邮件中得到了垃圾字符集(既不是日语也不是问号)。您能否帮忙看看我哪里做错了? - Akshada
@Akshada,你的附件名称应该是UTF-8编码的。但它可能不是。上面的代码并没有将字符串从任何编码转换为UTF-8。它只是指定了其实际编码,以便正确地将其转换为字节并再次转换为字符串。 - yurin
即使文件名包含国际字符,如德语umlaut(ae),也无法解决问题。 - benchpresser

3

这是我用来发送文件的示例代码(无论编码或数据结构如何)。

BodyPart fileBodyPart = new MimeBodyPart();
fileBodyPart.setDataHandler(new DataHandler(fileDataSource));
fileBodyPart.setFileName(attachment.getName());
fileBodyPart.setHeader("Content-Type", fileDataSource.getContentType());
fileBodyPart.setHeader("Content-ID", attachment.getName());
fileBodyPart.setDisposition(Part.INLINE);

这里的fileDataSource是一个javax.activation.DataSource(文本文件将在此处),

fileBodyPart.setDisposition(Part.INLINE); (PART.INLINE表示数据源与消息正文一起内嵌,就像HTML邮件一样,PART.ATTACHMENT表示数据源为附件)。

希望这可以帮到你。


问题是我没有一个文件,我需要能够将一个字符串作为附件发送。 - Ibolit
2
如果我没记错的话,您可以使用一个接收InputStream的数据源。您可以使用包含您字符串的InputStream,或将包含您字符串的临时文件传递给数据源。明天早上我得向您展示一下。 - Buhake Sindi
附件文件名的编码是否有描述头?当使用非拉丁字符的文件名时,我的显示为问号。 - theyuv

2

还有一个可能性:

String attachment = "älytöntä";
MimeBodyPart part = new MimeBodyPart();
part.setText(attachment, "UTF-8");
part.setDisposition("attachment");
part.setFileName("attachment.txt");
part.setHeader("Content-Transfer-Encoding", "base64");
part.setHeader("Content-type", "text/plain; charset=utf-8");

1

试一下这个:

String attachment = "Привет";
DataSource ds = new ByteArrayDataSource(attachment, "text/plain; charset=UTF-8");
messageBodyPart.setDataHandler(new DataHandler(ds));

更新:(完整示例)

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.Session;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;

public class Main {
    public static void main(String[] args) throws Exception {
        String attachment = "Привет";
        DataSource ds = new ByteArrayDataSource(attachment, "text/plain; charset=UTF-8");
        MimeBodyPart attachmentPart = new MimeBodyPart();
        attachmentPart.setDataHandler(new DataHandler(ds));

        MimeBodyPart bodyPart = new MimeBodyPart();
        bodyPart.setText("Hello this is some text");

        MimeMultipart mp = new MimeMultipart("mixed");
        mp.addBodyPart(bodyPart);
        mp.addBodyPart(attachmentPart);

        MimeMessage msg = new MimeMessage((Session)null);
        msg.setContent(mp);

        msg.writeTo(System.out);
    }
}

输出:

Message-ID: <1439781957.1.1297366787857.JavaMail.dnault@dnault.local>
MIME-Version: 1.0
Content-Type: multipart/mixed; 
    boundary="----=_Part_0_1579321858.1297366787792"

------=_Part_0_1579321858.1297366787792
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Hello this is some text
------=_Part_0_1579321858.1297366787792
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: base64

0J/RgNC40LLQtdGC
------=_Part_0_1579321858.1297366787792--

我刚试了一下,它不起作用,... = new ByteArrayDataSource(attachment.getBytes("UTF-8") ... 也不行。 - Ibolit
你能发布接收电子邮件的原始源代码吗? - dnault
如果你在这里得不到答案,可以尝试JavaMail论坛。Bill Shannon提供了令人惊叹的支持水平。http://forums.oracle.com/forums/forum.jspa?forumID=975 - dnault
原始数据源(省略开头): Content-Type: multipart/mixed; boundary=.. --002354867bdc4c631e049bf21caf Content-Type: text/plain; charset=ISO-8859-1; format=flowed; delsp=yes暂无内容--002354867bdc4c631e049bf21caf Content-Type: text/plain; charset=US-ASCII; name="2011_1_10_18_54_25_835.txt" Content-Disposition: attachment; filename="2011_1_10_18_54_25_835.txt" Content-Transfer-Encoding: base64Pz8/Pz8/LCA/ID8/Pz8/ID8/Pz8/Pw== --002354867bdc4c631e049bf21caf--很奇怪,因为我已将编码设置为UTF-8...setHeader("Content-Type", "text/plain; charset=UTF-8"); - Ibolit
我想知道你的问题是否与编译器的字符编码有关?https://dev59.com/uFTTa4cB1Zd3GeqPpRx8 - dnault

1

这个有效:

        MimeMessage msg = new MimeMessage(session);
        msg.setFrom(sendFrom);
        msg.setSubject(subject, "utf-8");
        msg.setSentDate(new Date());

        // create and fill the first message part
        MimeBodyPart mbp1 = new MimeBodyPart();
        mbp1.setContent(message,"text/plain; charset=UTF-8");
        // mbp1.setContent(message,"text/html; charset=UTF-8"); // with this the attachment will fail

        // create the Multipart and its parts to it
        Multipart mp = new MimeMultipart();
        mp.addBodyPart(mbp1);

        if (attachment!=null){
            // Part two is attachment
            MimeBodyPart mbp2 = new MimeBodyPart();
            mbp2 = new MimeBodyPart();

            DataSource ds = null;
            try {
                ds = new ByteArrayDataSource(attachment.getBytes("UTF-8"), "application/octet-stream");
                } catch (IOException e) {
                e.printStackTrace();
            }
            mbp2.setDataHandler(new DataHandler(ds));
            mbp2.addHeader("Content-Type", "text/plain; charset=\"UTF-8\"");
            mbp2.addHeader("Content-Transfer-Encoding", "base64");

            mbp2.setFileName("attachment.txt");
            mbp2.setDisposition(Part.ATTACHMENT);
            mp.addBodyPart(mbp2);
        }

        // add the Multipart to the message
        msg.setContent(mp);
        msg.saveChanges();

        // send the message
        Transport.send(msg);

0

我曾经尝试过在URL编码中发送文件名。对于Gmail而言,它是有效的。

messageBodyPart.setFileName(UriUtils.encodePath(attachment.getAttachmentName(), "UTF-8"))

完整代码在这里:

if (!CollectionUtils.isEmpty(requestMessage.getAttachments())) {
            MimeBodyPart messageBodyPart;
            String fileName;
            File file;
            for (Attachment attachment : requestMessage.getAttachments()) {
                messageBodyPart = new MimeBodyPart();
                fileName = attachment.getAttachmentName();
                file = new File(fileName);
                FileUtils.writeByteArrayToFile(file, attachment.getAttachment());
                messageBodyPart.setDataHandler(new DataHandler(new FileDataSource(file)));
                messageBodyPart.setFileName(UriUtils.encodePath(attachment.getAttachmentName(), "UTF-8"));
                messageBodyPart.setDisposition(Part.ATTACHMENT);
                multipart.addBodyPart(messageBodyPart);
            }
        }

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