安卓手机-无法在邮件中添加附件

12

默认情况下,保存在内部存储中的文件仅限于您的应用程序使用,并且其他应用程序无法访问它们(用户也无法访问)。

我可以在DDMS的文件浏览器中看到文件“/ data /data/package_name/files/”,但是当我使用Android的默认电子邮件API将上述文件URI附加到imageUri时,我发现附加的文件大小为0kb。

有人能建议我如何附加一个对应用程序私有的文件到电子邮件中吗?

虽然我成功将文件保存在SD卡中并从SD卡附加了文件,但这只适用于SD卡可用的情况。

但是如果SD卡不可用并且需要将文件保存到内部存储中,那么我该如何将其附加到电子邮件中呢?

String FILENAME = "hello_file.txt";
String string = "hello world!";FileOutputStream fos = openFileOutput(FILENAME,     Context.MODE_PRIVATE);
fos.write(string.getBytes());
fos.close();

File imageFile = getFileStreamPath(FILENAME );
Uri imageUri = Uri.fromFile(imageFile);

final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.setType("*/*");
emailIntent.putExtra(android.content.Intent.EXTRA_STREAM,imageUri);

this.startActivityForResult(Intent.createChooser(emailIntent, "Send mail..."),SUB_ACTIVITY);

我遇到了类似的问题。从SD卡中可以正常工作...但是从内部存储中,我没有成功实现这个功能。也许尝试调整MIME类型会有所帮助... - Mike
请问您能否详细说明一下吗? - pankaj
我个人使用以下代码发送带有 .html 附件的邮件:Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND); emailIntent.setType("text/html"); emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, "得分卡"); File sdDir = Environment.getExternalStorageDirectory(); File file = new File(sdDir, Preferences.PATH_TO_SAVE_ON_SD_CARD + "/scoreHtml.html"); emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file)); emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); context.startActivity(Intent.createChooser(emailIntent, "电子邮件:")); - Mike
但是在上面的代码中,使用了SD卡。我想将图像附加到电子邮件中,而不将其存储到SD卡中。 - pankaj
1
你是否曾经遇到过这个问题的解决方案? - Pinser
7个回答

6
当您尝试从内部存储附加文件时,GMail会将错误写入日志:
ERROR/Gmail(...): file:// attachment paths must point to file:///mnt/sdcard. 

电子邮件应用程序会显示附件,即使它在物理上不存在。
至于外部存储,文档说明如下:
每个兼容Android的设备都支持一个共享的“外部存储”,您可以使用它来保存文件。这可以是可移动存储介质(例如SD卡)或内部(不可移动)存储。
这意味着您无需担心设备完全没有外部存储。但是,有时外部存储可能不可用。请参阅 http://developer.android.com/guide/topics/data/data-storage.html#filesExternal

谢谢!我没想到要更改LogCat窗口上的过滤器,所以一直无法找出失败的原因。 - Kenny Wyland

5
Android: Attaching files from internal cache to Gmail




package com.stephendnicholas.gmailattach; 

import java.io.File; 
import java.io.FileNotFoundException; 

import android.content.ContentProvider; 
import android.content.ContentValues; 
import android.content.UriMatcher; 
import android.database.Cursor; 
import android.net.Uri; 
import android.os.ParcelFileDescriptor; 
import android.util.Log; 

public class CachedFileProvider extends ContentProvider { 

    private static final String CLASS_NAME = "CachedFileProvider"; 

    // The authority is the symbolic name for the provider class 
    public static final String AUTHORITY = "com.stephendnicholas.gmailattach.provider"; 

    // UriMatcher used to match against incoming requests 
    private UriMatcher uriMatcher; 

    @Override
    public boolean onCreate() { 
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 

        // Add a URI to the matcher which will match against the form 
        // 'content://com.stephendnicholas.gmailattach.provider/*' 
        // and return 1 in the case that the incoming Uri matches this pattern 
        uriMatcher.addURI(AUTHORITY, "*", 1); 

        return true; 
    } 

    @Override
    public ParcelFileDescriptor openFile(Uri uri, String mode) 
            throws FileNotFoundException { 

        String LOG_TAG = CLASS_NAME + " - openFile"; 

        Log.v(LOG_TAG, 
                "Called with uri: '" + uri + "'." + uri.getLastPathSegment()); 

        // Check incoming Uri against the matcher 
        switch (uriMatcher.match(uri)) { 

        // If it returns 1 - then it matches the Uri defined in onCreate 
        case 1: 

            // The desired file name is specified by the last segment of the 
            // path 
            // E.g. 
            // 'content://com.stephendnicholas.gmailattach.provider/Test.txt' 
            // Take this and build the path to the file 
            String fileLocation = getContext().getCacheDir() + File.separator 
                    + uri.getLastPathSegment(); 

            // Create & return a ParcelFileDescriptor pointing to the file 
            // Note: I don't care what mode they ask for - they're only getting 
            // read only 
            ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File( 
                    fileLocation), ParcelFileDescriptor.MODE_READ_ONLY); 
            return pfd; 

            // Otherwise unrecognised Uri 
        default: 
            Log.v(LOG_TAG, "Unsupported uri: '" + uri + "'."); 
            throw new FileNotFoundException("Unsupported uri: "
                    + uri.toString()); 
        } 
    } 

    // ////////////////////////////////////////////////////////////// 
    // Not supported / used / required for this example 
    // ////////////////////////////////////////////////////////////// 

    @Override
    public int update(Uri uri, ContentValues contentvalues, String s, 
            String[] as) { 
        return 0; 
    } 

    @Override
    public int delete(Uri uri, String s, String[] as) { 
        return 0; 
    } 

    @Override
    public Uri insert(Uri uri, ContentValues contentvalues) { 
        return null; 
    } 

    @Override
    public String getType(Uri uri) { 
        return null; 
    } 

    @Override
    public Cursor query(Uri uri, String[] projection, String s, String[] as1, 
            String s1) { 
        return null; 
    } 
}




<provider android:name="CachedFileProvider" android:authorities="com.stephendnicholas





public static void createCachedFile(Context context, String fileName, 
            String content) throws IOException { 

    File cacheFile = new File(context.getCacheDir() + File.separator 
                + fileName); 
    cacheFile.createNewFile(); 

    FileOutputStream fos = new FileOutputStream(cacheFile); 
    OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF8"); 
    PrintWriter pw = new PrintWriter(osw); 

    pw.println(content); 

    pw.flush(); 
    pw.close(); 
}




public static Intent getSendEmailIntent(Context context, String email, 
            String subject, String body, String fileName) { 

    final Intent emailIntent = new Intent( 
                android.content.Intent.ACTION_SEND); 

    //Explicitly only use Gmail to send 
    emailIntent.setClassName("com.google.android.gm","com.google.android.gm.ComposeActivityGmail"); 

    emailIntent.setType("plain/text"); 

    //Add the recipients 
    emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, 
                new String[] { email }); 

    emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject); 

    emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, body); 

    //Add the attachment by specifying a reference to our custom ContentProvider 
    //and the specific file of interest 
    emailIntent.putExtra( 
            Intent.EXTRA_STREAM, 
                Uri.parse("content://" + CachedFileProvider.AUTHORITY + "/"
                        + fileName)); 

    return emailIntent; 
}

    enter code here

正是我所需要的,但添加源代码会更好:http://stephendnicholas.com/archives/974。我也发现它很有用,因为它更加结构化。 - Christoph Sonntag

4
为了分享一个私人文件,你需要使用ContentProvider来提供其他应用程序访问你的文件。这里有一个很好的例子:Android: Attaching files from internal cache to Gmail
此外,虽然教程提到你需要在Android清单文件中声明提供者,但它没有指定它应该包含在<application>中,所以请确保在声明时它位于<application> </application>内部。

3

这段代码可以帮助你了解附件的相关信息:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    buttonSend = (Button) findViewById(R.id.buttonSend);

    textTo = (EditText) findViewById(R.id.editTextTo);
    textSubject = (EditText) findViewById(R.id.editTextSubject);
    textMessage = (EditText) findViewById(R.id.editTextMessage);

    buttonSend.setOnClickListener( new OnClickListener() {
        @Override
        public void onClick(View v) {
            String to = textTo.getText().toString();
            String subject = textSubject.getText().toString();
            String message = textMessage.getText().toString();

            Intent i = new Intent(Intent.ACTION_SEND);
            i.setType("plain/text");
            File data = null;

            try {
                Date dateVal = new Date();
                String filename = dateVal.toString();
                data = File.createTempFile("Report", ".csv");
                FileWriter out = (FileWriter) GenerateCsv.generateCsvFile(
                                                data, "Name,Data1");
                i.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(data));
                i.putExtra(Intent.EXTRA_EMAIL, new String[] { to });
                i.putExtra(Intent.EXTRA_SUBJECT, subject);
                i.putExtra(Intent.EXTRA_TEXT, message);
                startActivity(Intent.createChooser(i, "E-mail"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    });
}

public class GenerateCsv
{
    public static FileWriter generateCsvFile(File sFileName,String fileContent) 
    {
        FileWriter writer = null;

        try {
            writer = new FileWriter(sFileName);
            writer.append(fileContent);
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return writer;
    }
}

以上代码需要您将以下权限添加到您的清单文件中:
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>

1
我也遇到了使用内部文件的问题,尽管我已经在 /data/data//files/testFileName.txt 上使用了 MODE_WORLD_READABLE 的 openFileInput,并且在 URI.parse 中使用了额外的“/”(见下文),但是收到的测试邮件仍然缺少所需的附件。非常抱歉,除了尝试使用 SD 卡上的外部文件之外,没有答案 - 这是我的下一个实验!

代码:

File tmpFile = new File(context.getFilesDir(), mfileName);
        Log.d(TAG, tmpFile.toString());  
// This shows: /data/data/org.eddiem.adeveloper.flatfiletest/files/testFile.csv 
        //File tmpFile2 = new File(context.getFileStreamPath(mfileName), mfileName);
        //Log.v(TAG, tmpFile2.toString());
// This also shows: /data/data/org.eddiem.adeveloper.flatfiletest/files/testFile.csv 

        //Uri uri = Uri.fromFile(new File(context.getFileStreamPath(mfileName), mfileName));
        Uri uri = Uri.parse("file://" + tmpFile.toString()); 
        //Uri uri = Uri.fromFile(new File(Environment.getExternalStorageDirectory(), 
        //                              mfileName));
        Log.d(TAG, "Uri-path is: " + uri.getPath()); // or .toString()

        Intent i = new Intent(android.content.Intent.ACTION_SEND);
        i.setType("text/plain");
        i.putExtra(Intent.EXTRA_EMAIL, new String[]{"eddie.moxey@sky.com"});
        i.putExtra(Intent.EXTRA_SUBJECT, "Test Email - with Attachment");
        i.putExtra(Intent.EXTRA_TEXT, "This is a test Email with an Attachment.");
        i.putExtra(Intent.EXTRA_STREAM, uri);
        //startActivity(Intent.createChooser(i, "Select application"));
        startActivity(Intent.createChooser(i, "Send mail"));

1

尝试在保存文件时使用Context.MODE_WORLD_READABLE而不是Context.MODE_PRIVATE。这样其他应用程序将能够访问该文件。


@ ,我已经尝试过 MODE_WORLD_WRITEABLE,但它不起作用。 - pankaj

0

我之前遇到了同样的问题,以下方法对我有用。

首先发送广播通知设备文件已创建/挂载。

例如:

sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,Uri.parse("file://"+storagePath)));

然后使用代码发送带有附件的电子邮件。

Intent email = new Intent(Intent.ACTION_SEND);
email.putExtra(Intent.EXTRA_EMAIL,  "Receiver Email Address" );
email.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
email.putExtra(Intent.EXTRA_SUBJECT, "Subject");
email.putExtra(Intent.EXTRA_TEXT,"Email Text");

//Mime type of the attachment (or) u can use sendIntent.setType("*/*")
//email.setType("text/plain");
email.setType("application/YourMimeType");

//Full Path to the attachment
email.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://"+storagePath));
try
{
    startActivity(Intent.createChooser(email, "Send Message..."));
}
catch (android.content.ActivityNotFoundException ex) 
{

}

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