使用 Gmail API 从 Uri 通过电子邮件在 Android 中发送文件时损坏 excel 文件

Corrupted excel file when emailing it in Android from a Uri using the Gmail API

我正在尝试从 Microsoft 的 Excel Android 应用程序共享到我的自定义 Android 应用程序,它将获取共享的 excel 工作簿并通过电子邮件发送通过 Gmail 的 API.

在我的应用程序发送电子邮件并尝试在我的计算机上打开 excel 文件后,出现错误,“我们发现 'May 25.xlsx' 中的某些内容存在问题”。

但是,它确实可以使用 Google 表格在桌面 Chrome 浏览器中打开。

这是工作簿的 URI:content://com.microsoft.office.excel.excelApplication.provider/docsui_temp_share_files/fea009cf-65c3-46f4-8dda-50758298b9fc/May%2025.xlsx

这是将其转换为文件的代码。

Context ctx; //defined by Android
Uri uri; //comes in populated with the value above
String filename; //generated from the Uri in a separate method

InputStream is = ctx.getContentResolver().openInputStream(uri);
File directory = ctx.getCacheDir();
FileOutputStream fos = null;
File fileToReturn = null;
try {
    final File tempFile = File.createTempFile(filename.split("\.")[0], "." + filename.split("\.")[1], directory);
    tempFile.deleteOnExit();

    fos = new FileOutputStream(tempFile);

    byte[] bytes = new byte[1024 * 16];
    int read = -1;
    while ((read = is.read(bytes)) != -1) {
        fos.write(bytes);
    }

    fileToReturn = tempFile;

}
finally {
    if (fos != null) {
        fos.close();
    }
}

这是创建要发送的 MimeMessage 的代码。附件参数具有从上面创建的文件。

private MimeMessage createEmail(List<String> toAddresses,
                                String from,
                                String subject,
                                String bodyText, ArrayList<File> attachments) throws MessagingException {
    if(attachments == null){
        attachments = new ArrayList<>();
    }
    Properties props = new Properties();
    Session session = Session.getDefaultInstance(props, null);

    MimeMessage email = new MimeMessage(session);
    InternetAddress fAddress = new InternetAddress(from);

    for(String toAddress : toAddresses) {
        InternetAddress toInternetAddress = new InternetAddress(toAddress);
        email.addRecipient(javax.mail.Message.RecipientType.TO, toInternetAddress );
    }

    email.setFrom(fAddress);
    email.setSubject(subject);

    Multipart multipart = new MimeMultipart();

    BodyPart textBody = new MimeBodyPart();
    textBody.setText(bodyText);
    multipart.addBodyPart(textBody);

    for (File attachment : attachments){
            MimeBodyPart attachmentBody = new MimeBodyPart();
            DataSource source = new FileDataSource(attachment.getAbsolutePath());
            attachmentBody.setDataHandler(new DataHandler(source));
            attachmentBody.setFileName(attachment.getName());
            multipart.addBodyPart(attachmentBody);
    }

    email.setContent(multipart);
    return email;
}

以及发送电子邮件的代码。

private String sendMessage(Gmail service,
                           String userId,
                           MimeMessage email)
        throws MessagingException, IOException {

    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    email.writeTo(bytes);

    ByteArrayContent messageByteArrayContent = new ByteArrayContent("message/rfc822", bytes.toByteArray());
    
    Message message;
    message = service.users().messages().send(userId, null, messageByteArrayContent).execute();
}

这是电子邮件的内容。

MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="----=_Part_0_262386509.1596398610889"
Date: Sun, 2 Aug 2020 13:03:34 -0700

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

May 252265647717182285060.xlsx
------=_Part_0_262386509.1596398610889
Content-Type: application/octet-stream; name="May 25.xlsx"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="May 25.xlsx"


------=_Part_0_262386509.1596398610889--

请注意 Content-Type 是 application/octect-stream。

我通过手动添加 Content-Type header 实现了 application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,但无济于事。

此代码几乎可以发送任何其他内容。如果我先保存 excel 文件并从我的文件管理器应用程序共享它,它就可以正常打开。

而且它似乎特定于 excel 文件。如果我从 Excel 应用程序(这是一个选项)将其共享为 pdf,则 pdf 可以正常打开。

如果我尝试从 Google 表格应用共享并将其作为 .xlsx 文件共享,我也会遇到同样的错误。但同样,如果我尝试将其作为其他任何内容(例如 .csv 或 .pdf)进行共享,它在另一侧可以正常打开。

这是电子邮件,如果它是从 Google 表格应用程序共享的 .pdf 文件。同样,内容类型是 application/octet-stream,但在这里可以正常打开。

MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="----=_Part_4_203349844.1596399067869"
Date: Sun, 2 Aug 2020 13:11:10 -0700

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

Untitled spreadsheet8410545067288443069.pdf
------=_Part_4_203349844.1596399067869
Content-Type: application/octet-stream; name="Untitled spreadsheet8410545067288443069.pdf"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="Untitled spreadsheet8410545067288443069.pdf"


------=_Part_4_203349844.1596399067869--

在编写 xlsx 文件的代码中,您总是读取指定的缓冲区大小 (1024 * 16),然后写入文件。我认为在上次读取时,如果您读取的字节小于字节数组的缓冲区大小,您将写入额外的不必要字节。这可能是您的文件损坏原因。