【发布时间】:2017-07-28 21:01:45
【问题描述】:
我查看了很多很多 SO 帖子以找到答案,但没有运气......
如何将公共存储中的文件作为电子邮件附件发送?
大多数帖子都在询问内部存储文件。因此,他们使用FileProvider(或类似的东西)。在FileProvider 的文档中,我读到:
FileProvider ...促进与应用程序关联的文件的安全共享...
但我要发送的文件与我的应用没有关联。它是 Documents 文件夹中公共存储中的 .csv 文件。因此,我的应用程序设置一个具有公共文件访问权限的 FileProvider 似乎真的很奇怪。更奇怪的是使用我的包名作为“权限”。
我尝试了以下代码,这些代码是根据其他 1 或 2 个帖子中的答案汇总而成的:
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
File f = new File(path, _sFileName);
_sFileDir = f.getCanonicalPath();
我有另一种方法:
Intent i = new Intent(android.content.Intent.ACTION_SEND);
String Email[] = { "my.name@gmail.com" };
i.putExtra(android.content.Intent.EXTRA_EMAIL, Email);
i.putExtra(android.content.Intent.EXTRA_SUBJECT, "My Report");
i.setType("plain/text");
String sURI = "file:/" + _sFileDir;
i.putExtra(Intent.EXTRA_STREAM, Uri.parse(sURI));
startActivity(i);
这会导致应用崩溃并显示以下消息:“MyApp 已停止”
当我将“file:/”更改为“content:/”时,应用程序不再崩溃,并且发送了电子邮件,但没有附加文件。
我的 AndroidManifest.xml 包含以下权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
这是我使用“content:/”时的 LogCat:
07-28 17:19:22.528 27809-27809/? I/art: Late-enabling -Xcheck:jni
07-28 17:19:22.569 27809-27816/? E/art: Failed sending reply to debugger: Broken pipe
07-28 17:19:22.569 27809-27816/? I/art: Debugger is no longer active
07-28 17:19:22.569 27809-27816/? I/art: Starting a blocking GC Instrumentation
07-28 17:19:22.582 27809-27809/? W/ActivityThread: Application com.myorg.myapp is waiting for the debugger on port 8100...
07-28 17:19:22.583 27809-27809/? I/System.out: Sending WAIT chunk
07-28 17:19:24.636 27809-27816/com.myorg.myapp I/art: Debugger is active
07-28 17:19:24.785 27809-27809/com.myorg.myapp I/System.out: Debugger has connected
07-28 17:19:24.785 27809-27809/com.myorg.myapp I/System.out: waiting for debugger to settle...
07-28 17:19:24.985 27809-27809/com.myorg.myapp I/System.out: waiting for debugger to settle...
07-28 17:19:25.186 27809-27809/com.myorg.myapp I/System.out: waiting for debugger to settle...
07-28 17:19:25.386 27809-27809/com.myorg.myapp I/System.out: waiting for debugger to settle...
07-28 17:19:25.588 27809-27809/com.myorg.myapp I/System.out: waiting for debugger to settle...
07-28 17:19:25.788 27809-27809/com.myorg.myapp I/System.out: waiting for debugger to settle...
07-28 17:19:25.989 27809-27809/com.myorg.myapp I/System.out: waiting for debugger to settle...
07-28 17:19:26.189 27809-27809/com.myorg.myapp I/System.out: debugger has settled (1454)
07-28 17:19:26.408 27809-27809/com.myorg.myapp W/System: ClassLoader referenced unknown path: /data/app/com.myorg.myapp-1/lib/arm
07-28 17:19:26.444 27809-27809/com.myorg.myapp I/InstantRun: starting instant run server: is main process
07-28 17:19:26.508 27809-27816/com.myorg.myapp I/art: Starting a blocking GC Instrumentation
07-28 17:19:26.801 27809-27809/com.myorg.myapp W/art: Before Android 4.1, method android.graphics.PorterDuffColorFilter android.support.graphics.drawable.VectorDrawableCompat.updateTintFilter(android.graphics.PorterDuffColorFilter, android.content.res.ColorStateList, android.graphics.PorterDuff$Mode) would have incorrectly overridden the package-private method in android.graphics.drawable.Drawable
07-28 17:19:28.471 27809-27814/com.myorg.myapp I/art: Do partial code cache collection, code=14KB, data=23KB
07-28 17:19:28.471 27809-27814/com.myorg.myapp I/art: After code cache collection, code=14KB, data=23KB
07-28 17:19:28.471 27809-27814/com.myorg.myapp I/art: Increasing code cache capacity to 128KB
07-28 17:19:28.475 27809-27814/com.myorg.myapp I/art: Do partial code cache collection, code=14KB, data=42KB
07-28 17:19:28.476 27809-27814/com.myorg.myapp I/art: After code cache collection, code=14KB, data=42KB
07-28 17:19:28.476 27809-27814/com.myorg.myapp I/art: Increasing code cache capacity to 256KB
07-28 17:19:29.036 27809-27814/com.myorg.myapp I/art: Do full code cache collection, code=119KB, data=108KB
07-28 17:19:29.037 27809-27814/com.myorg.myapp I/art: After code cache collection, code=113KB, data=86KB
07-28 17:19:29.216 27809-27895/com.myorg.myapp I/Adreno: QUALCOMM build : 7d18700, I8ee426a9a2
Build Date : 10/07/16
OpenGL ES Shader Compiler Version: XE031.09.00.03
Local Branch : mybranch22308589
Remote Branch : quic/LA.BR.1.3.6_rb1.6
Remote Branch : NONE
Reconstruct Branch : NOTHING
07-28 17:19:29.229 27809-27895/com.myorg.myapp I/OpenGLRenderer: Initialized EGL, version 1.4
07-28 17:19:29.229 27809-27895/com.myorg.myapp D/OpenGLRenderer: Swap behavior 1
07-28 17:19:29.388 27809-27814/com.myorg.myapp I/art: Do partial code cache collection, code=125KB, data=105KB
07-28 17:19:29.388 27809-27814/com.myorg.myapp I/art: After code cache collection, code=125KB, data=105KB
07-28 17:19:29.388 27809-27814/com.myorg.myapp I/art: Increasing code cache capacity to 512KB
07-28 17:19:29.691 27809-27809/com.myorg.myapp W/art: Before Android 4.1, method int android.support.v7.widget.ListViewCompat.lookForSelectablePosition(int, boolean) would have incorrectly overridden the package-private method in android.widget.ListView
07-28 17:19:29.701 27809-27809/com.myorg.myapp I/Choreographer: Skipped 97 frames! The application may be doing too much work on its main thread.
07-28 17:19:34.479 27809-27809/com.myorg.myapp I/Choreographer: Skipped 124 frames! The application may be doing too much work on its main thread.
07-28 17:19:37.417 27809-27809/com.myorg.myapp W/IInputConnectionWrapper: reportFullscreenMode on inexistent InputConnection
07-28 17:19:37.417 27809-27809/com.myorg.myapp W/IInputConnectionWrapper: finishComposingText on inactive InputConnection
07-28 17:19:41.841 27809-27814/com.myorg.myapp I/art: Do full code cache collection, code=252KB, data=246KB
07-28 17:19:41.844 27809-27814/com.myorg.myapp I/art: After code cache collection, code=248KB, data=216KB
07-28 17:19:45.257 27809-27809/com.myorg.myapp I/Choreographer: Skipped 204 frames! The application may be doing too much work on its main thread.
07-28 17:19:49.125 27809-27809/com.myorg.myapp W/IInputConnectionWrapper: reportFullscreenMode on inexistent InputConnection
07-28 17:19:49.126 27809-27809/com.myorg.myapp W/IInputConnectionWrapper: finishComposingText on inactive InputConnection
07-28 17:19:57.498 27809-27814/com.myorg.myapp I/art: Do partial code cache collection, code=250KB, data=227KB
07-28 17:19:57.499 27809-27814/com.myorg.myapp I/art: After code cache collection, code=250KB, data=227KB
07-28 17:19:57.499 27809-27814/com.myorg.myapp I/art: Increasing code cache capacity to 1024KB
07-28 17:20:12.117 27809-27809/com.myorg.myapp W/IInputConnectionWrapper: reportFullscreenMode on inexistent InputConnection
07-28 17:20:12.117 27809-27809/com.myorg.myapp W/IInputConnectionWrapper: finishComposingText on inactive InputConnection
这是另一个使用“file:/”且应用崩溃时的 LogCat:
07-28 17:43:57.190 30651-30651/com.myorg.myapp D/AndroidRuntime: Shutting down VM
07-28 17:43:57.232 30651-30651/com.myorg.myapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.myorg.myapp, PID: 30651
java.lang.IllegalStateException: Could not execute method for android:onClick
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:293)
at android.view.View.performClick(View.java:5612)
at android.view.View$PerformClick.run(View.java:22285)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6123)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288)
at android.view.View.performClick(View.java:5612)
at android.view.View$PerformClick.run(View.java:22285)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6123)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
Caused by: android.os.FileUriExposedException: file://storage/emulated/0/Documents/Tasks.csv exposed beyond app through ClipData.Item.getUri()
at android.os.StrictMode.onFileUriExposed(StrictMode.java:1813)
at android.net.Uri.checkFileUriExposed(Uri.java:2360)
at android.content.ClipData.prepareToLeaveProcess(ClipData.java:832)
at android.content.Intent.prepareToLeaveProcess(Intent.java:8957)
at android.content.Intent.prepareToLeaveProcess(Intent.java:8942)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1583)
at android.app.Activity.startActivityForResult(Activity.java:4228)
at android.support.v4.app.BaseFragmentActivityJB.startActivityForResult(BaseFragmentActivityJB.java:50)
at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:79)
at android.app.Activity.startActivityForResult(Activity.java:4187)
at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:859)
at android.app.Activity.startActivity(Activity.java:4515)
at android.app.Activity.startActivity(Activity.java:4483)
at com.myorg.myapp.MainActivity.onClickSend(ProgSummary.java:209)
at java.lang.reflect.Method.invoke(Native Method)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288)
at android.view.View.performClick(View.java:5612)
at android.view.View$PerformClick.run(View.java:22285)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6123)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
这样做的正确方法是什么?
新信息:
当我使用“content:/”时,这一次我在 Gmail 应用程序上看到一条消息(Toast?)短暂闪烁,说“无法附加空文件”。但它不是空的!!!
更多新信息:
我尝试使用 FileProvider 来执行此操作。这是代码...
AndroidManifest.xml:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.myorg.myapp.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
文件路径.xml:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="publicdocuments/" path="documents/" />
</paths>
MainActivity.java:
public void onClickSend(View v) {
Intent i = new Intent(android.content.Intent.ACTION_SEND);
String Email[] = { "my.address@gmail.com" };
i.putExtra(android.content.Intent.EXTRA_EMAIL, Email);
i.putExtra(android.content.Intent.EXTRA_SUBJECT, "Report");
i.setType("text/plain");
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
File f = new File(path, "file.csv");
Uri sUri = FileProvider.getUriForFile(MainActivity.this, "com.myorg.myapp.fileprovider", f);
i.setData(sUri);
i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(i);
}
现在的问题是它不起作用 - 我的应用在调用 FileProvider.getUriForFile() 时再次崩溃。
这是新的 LogCat:
--------- beginning of crash
07-29 13:45:27.116 10397-10397/com.myorg.myapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.myorg.myapp, PID: 10397
java.lang.IllegalStateException: Could not execute method for android:onClick
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:293)
at android.view.View.performClick(View.java:5612)
at android.view.View$PerformClick.run(View.java:22285)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6123)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288)
at android.view.View.performClick(View.java:5612)
at android.view.View$PerformClick.run(View.java:22285)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6123)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
Caused by: java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Documents/Tasks.csv
at android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:711)
at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:400)
at com.myorg.myapp.MainActivity.onClickSend(MainActivity.java:214)
at java.lang.reflect.Method.invoke(Native Method)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288)
at android.view.View.performClick(View.java:5612)
at android.view.View$PerformClick.run(View.java:22285)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6123)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
如您所见,有一个InvocationTargetException,但我不知道为什么......
附加信息:在 FileProvider.getUriForFile() 中将 MainActivity.this 替换为 v.getContext() 会得到相同的结果。
【问题讨论】:
-
看看 LogCat Stacktrace
-
我认为您忘记了读取外部存储的使用权限..并检查您是否在应用程序设置中启用了存储权限..
-
谢谢@mmmatey,我已经更新了我的代码(上面)以包含权限。
-
好的,到目前为止一切顺利,您可以在这里粘贴 log cat 吗.. 或者尝试从您当前的 sURI 制作文件,并检查文件是否确实存在? "new File(sUri).exists" ..如果返回false,文件不在这里,或路径不正确或权限不允许应用程序使用,请使用此文件..
-
并且还尝试替换,getCanonicalPath(),只使用getPath()..