Android应用使用OKHttp调用SendGrid发送带附件邮件问题咨询
Hey there! Let’s tackle this SendGrid + OkHttp attachment problem you’re facing. The good news is you don’t have to use byte arrays exclusively—there are workarounds depending on your use case. Here are the most practical solutions:
1. Troubleshoot the "file send" method first
Chances are the file approach isn’t working because of incorrect MultipartBody setup or Android storage permissions, not because OkHttp can’t handle files. SendGrid’s API expects specific fields for attachments, so double-check your request structure.
Here’s a corrected example of sending a file via OkHttp:
import okhttp3.* import java.io.File fun sendEmailWithAttachment(filePath: String) { val apiKey = "YOUR_SENDGRID_API_KEY" val file = File(filePath) // Verify file exists and is readable (critical for Android 10+ scoped storage) if (!file.exists() || !file.canRead()) { // Handle file access error (check permissions, use ContentResolver if needed) return } val fileRequestBody = RequestBody.create(MediaType.parse("application/pdf"), file) val multipartBody = MultipartBody.Builder() .setType(MultipartBody.FORM) // Email metadata .addFormDataPart("personalizations[0][to][0][email]", "recipient@example.com") .addFormDataPart("from[email]", "your-sender@example.com") .addFormDataPart("subject", "Test Email with Attachment") .addFormDataPart("content[0][type]", "text/plain") .addFormDataPart("content[0][value]", "Check out the attached file!") // Attachment fields (SendGrid requires these) .addFormDataPart("attachments[0][content]", "", fileRequestBody) .addFormDataPart("attachments[0][filename]", file.name) .addFormDataPart("attachments[0][type]", "application/pdf") .addFormDataPart("attachments[0][disposition]", "attachment") .build() val request = Request.Builder() .url("https://api.sendgrid.com/v3/mail/send") .header("Authorization", "Bearer $apiKey") .post(multipartBody) .build() val client = OkHttpClient() client.newCall(request).enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { // Handle network failure e.printStackTrace() } override fun onResponse(call: Call, response: Response) { // Check SendGrid's response code (202 means success) println("Response code: ${response.code()}") response.close() } }) }
Key fixes to check:
- Ensure your app has
READ_EXTERNAL_STORAGEpermission (or use scoped storage APIs for Android 10+) - Include all required SendGrid attachment fields:
content,filename,type, anddisposition - Verify the media type matches your file (e.g.,
image/pngfor images,application/vnd.openxmlformats-officedocument.spreadsheetml.sheetfor Excel files)
2. Convert input streams to temporary files (great for large files)
If you’re working with input streams (e.g., from assets, network, or in-memory data), you can write the stream to a temporary file in your app’s cache directory. This avoids loading the entire file into memory (which prevents OOM for large attachments).
Example code to convert an input stream to a temp file:
import android.content.Context import java.io.File import java.io.InputStream fun inputStreamToTempFile(inputStream: InputStream, context: Context): File { val tempFile = File.createTempFile("sendgrid_attachment", ".tmp", context.cacheDir) tempFile.outputStream().use { outputStream -> inputStream.copyTo(outputStream) } // Optional: Delete temp file after sending tempFile.deleteOnExit() return tempFile } // Usage (e.g., from assets) val inputStream = context.assets.open("report.pdf") val tempFile = inputStreamToTempFile(inputStream, context) sendEmailWithAttachment(tempFile.absolutePath)
3. Use byte arrays (for small files only)
If your attachment is small (under a few MB), converting the input stream to a byte array is a quick solution. Just be cautious—large files will cause memory issues.
Example:
fun inputStreamToByteArray(inputStream: InputStream): ByteArray { return inputStream.use { it.readBytes() } } // Usage val inputStream = ... // Your input stream val attachmentBytes = inputStreamToByteArray(inputStream) val byteRequestBody = RequestBody.create(MediaType.parse("application/pdf"), attachmentBytes) // Add to MultipartBody: multipartBody.addFormDataPart("attachments[0][content]", "", byteRequestBody) // Don't forget to add filename, type, and disposition fields as before
Final Recommendation
- For most cases: Fix the file send method first—it’s the most memory-efficient and aligns with OkHttp’s strengths.
- For input streams: Use temporary files if dealing with large data; reserve byte arrays for small attachments.
内容的提问来源于stack exchange,提问作者Kristy Welsh

