Android发送带附件SMTP邮件时触发File Not Found Exception求助
I've run into this exact issue before—here's what's going wrong and how to fix it:
The Root Cause
When you use Intent.ACTION_GET_CONTENT to pick a file, the returned Uri is a content URI (e.g., content://com.android.providers.media.documents/document/image%3A1234), not a direct file system path. If you try to use this URI as a file path for your SMTP client, it will throw a FileNotFoundException because that path doesn't exist in the device's file system.
Additionally, on Android 10+ (API 29+), Scoped Storage restricts direct access to file system paths, so even if you tried to convert the content URI to a real path, it might not work for all file types.
The Solution: Use Input Streams Instead of File Paths
The best approach is to use the ContentResolver to open an input stream directly from the content URI, then pass that stream to your SMTP attachment handler. This avoids needing a file path entirely and works with all content URIs, regardless of the source.
Here's a revised version of your code that implements this fix:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == RESULT_SUCCESS && resultCode == RESULT_OK && data != null) { Uri fileUri = data.getData(); new Thread(() -> { try { // Get access to the content resolver ContentResolver contentResolver = getContentResolver(); // Open an input stream from the content URI InputStream fileInputStream = contentResolver.openInputStream(fileUri); // Fetch the original filename of the attachment String attachmentFileName = getAttachmentFileName(contentResolver, fileUri); // Configure SMTP properties (fill in your details) Properties smtpProps = new Properties(); smtpProps.put("mail.smtp.host", "your_smtp_host"); // e.g., "smtp.gmail.com" smtpProps.put("mail.smtp.port", "587"); // Common TLS port smtpProps.put("mail.smtp.auth", "true"); smtpProps.put("mail.smtp.starttls.enable", "true"); // Create SMTP session with authentication Session session = Session.getInstance(smtpProps, new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("your_email@example.com", "your_email_password"); } }); // Build the email message MimeMessage message = new MimeMessage(session); message.setFrom(new InternetAddress("your_email@example.com")); message.setRecipients(Message.RecipientType.TO, InternetAddress.parse("recipient@example.com")); message.setSubject("Email with Attachment"); // Create the main message body MimeBodyPart messageBody = new MimeBodyPart(); messageBody.setText("Attached is the file you selected."); // Create the attachment part using the input stream MimeBodyPart attachmentBody = new MimeBodyPart(); // Use ByteArrayDataSource to wrap the input stream with the correct MIME type attachmentBody.setDataHandler(new DataHandler( new ByteArrayDataSource(fileInputStream, contentResolver.getType(fileUri)) )); attachmentBody.setFileName(attachmentFileName); // Combine body and attachment into a multipart message Multipart multipart = new MimeMultipart(); multipart.addBodyPart(messageBody); multipart.addBodyPart(attachmentBody); message.setContent(multipart); // Send the email Transport.send(message); fileInputStream.close(); // Don't forget to close the stream! // Update UI on the main thread runOnUiThread(() -> Toast.makeText(getApplicationContext(), "Email sent successfully!", Toast.LENGTH_SHORT).show() ); } catch (Exception e) { e.printStackTrace(); runOnUiThread(() -> Toast.makeText(getApplicationContext(), "Error sending email: " + e.getMessage(), Toast.LENGTH_LONG).show() ); } }).start(); } } // Helper method to get the filename from a content URI private String getAttachmentFileName(ContentResolver resolver, Uri uri) { String fileName = "attachment"; Cursor cursor = resolver.query(uri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null); if (cursor != null && cursor.moveToFirst()) { int nameColumnIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); if (nameColumnIndex != -1) { fileName = cursor.getString(nameColumnIndex); } cursor.close(); } return fileName; }
Key Notes
- Permission Check: Make sure you have the
INTERNETpermission declared in yourAndroidManifest.xml:
You don't need storage permissions here because<uses-permission android:name="android.permission.INTERNET" />ACTION_GET_CONTENTgrants temporary access to the selected file via the content URI. - Background Execution: Always run SMTP operations in a background thread (as shown in the example) to avoid blocking the main thread and causing ANRs.
- MIME Types: Using
contentResolver.getType(fileUri)ensures the attachment uses the correct MIME type, which helps email clients render the attachment properly.
内容的提问来源于stack exchange,提问作者Muhammad Huzaifa

