【问题标题】:Android photo sharing with FileProvider使用 FileProvider 共享 Android 照片
【发布时间】:2014-07-16 13:45:33
【问题描述】:

我已阅读有关此论点的所有答案,但我总是收到接收我照片的应用程序的错误。
对我对所有应用程序有效的唯一方法是(它有效因为 sd 卡文件对所有应用程序都是公开的):

final File tmpFile = new File(context.getExternalCacheDir(), "exported.jpg");
Uri tmpFileUri = Uri.fromFile(tmpFile);

Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.setDataAndType(tmpFileUri, "image/jpeg");
shareIntent.putExtra(Intent.EXTRA_STREAM, tmpFileUri);
context.startActivity(Intent.createChooser(shareIntent, context.getString(R.string.share_image)));



现在,我不知道如何共享位于私人文件夹中的文件。 我使用了谷歌文档提供的代码:

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.test.myapp.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true" >
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/filepaths" />
</provider>
...
...
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="internal_files" path="/"/>
    <cache-path name="internal_cache" path="/" />
</paths>


这是使用FileProvider 共享文件的代码,但不适用于任何应用程序,除了最新情况:

final File tmpFile = new File(context.getCacheDir(), "exported.jpg");
Uri tmpFileUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", tmpFile);
//Remove the uri permission because we overwrite the file
context.revokeUriPermission(tmpFileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

saveBitmapToPath(bitmap, tmpFile);
bitmap.recycle();

Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.setDataAndType(tmpFileUri, "image/jpeg");
shareIntent.putExtra(Intent.EXTRA_STREAM, tmpFileUri);
//Grant again the permissions
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
context.startActivity(Intent.createChooser(shareIntent, context.getString(R.string.share_image)));

为什么我在其他应用程序中总是出现错误,例如:
java.lang.SecurityException: Permission Denial: content://com.test.myapp.fileprovider/internal_cache/exported.jpg (pid=675, uid=10052) requires null
或者
IllegalArgumentException: Failed to find configuration root that contains content://com.test.myapp.fileprovider/internal_cache/exported.jpg

【问题讨论】:

    标签: java android android-sharing android-fileprovider


    【解决方案1】:

    终于看了下接收app的源码,得到了解决方案。
    这是我分享的完整的工作代码。
    我希望对某人有所帮助:

    <!-- AndroidManifest.xml -->
    <provider
        android:name="com.test.myapp.fileprovider.FileProvider"
        android:authorities="com.test.myapp.fileprovider"
        android:exported="true"
        tools:ignore="ExportedContentProvider" />
    


    //EntryPoint
    private void mySharer() {
        ArrayList<Uri> streamUris = new ArrayList<Uri>();
        for (int i = 0; i < 10; i++) {
            File tmpFile = new File(getContext().getCacheDir(), "tmp" + i + ".jpg");
            Uri tmp = FileProvider.getUriForFile("com.test.myapp.fileprovider", tmpFile);
            streamUris.add(tmp);
        }
    }
    
    //Share Intent creator
    public final void shareUris(ArrayList<Uri> streamUris) {
        if (!streamUris.isEmpty()) {
            Intent shareIntent = new Intent();
            shareIntent.putExtra(ShareCompat.EXTRA_CALLING_PACKAGE, getPackageName());
            shareIntent.putExtra(ShareCompat.EXTRA_CALLING_ACTIVITY, getComponentName());
            shareIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET | Intent.FLAG_GRANT_READ_URI_PERMISSION);
            shareIntent.setType("image/jpeg");
    
            if (streamUris.size() == 1) {
                shareIntent.setAction(Intent.ACTION_SEND);
                shareIntent.putExtra(Intent.EXTRA_STREAM, streamUris.get(0));
            } else {
                shareIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
                shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, streamUris);
            }
    
            //For multiple images copy all images in the baseDir and use startActivityForResult
            startActivityForResult(Intent.createChooser(shareIntent, getString(R.string.share_image)), 500);
        }
    }
    
    //onResult you can delete all temp images/files with specified extensions
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case 500:
                getContentResolver().delete(FileProvider.getUriForFile(getPackageName() + ".fileprovider", null), FileProvider.WHERE_EXTENSION, new String[]{"jpg"});
                break;
            default:
                break;
        }
    }
    
    /**
     * This class extends the ContentProvider
     */
    abstract class AbstractFileProvider extends ContentProvider {
    
        private final static String OPENABLE_PROJECTION_DATA = "_data";
        private final static String[] OPENABLE_PROJECTION = { OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE, OPENABLE_PROJECTION_DATA };
    
        @Override
        public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
            if (projection == null) {
                projection = OPENABLE_PROJECTION;
            }
    
            final MatrixCursor cursor = new MatrixCursor(projection, 1);
            MatrixCursor.RowBuilder b = cursor.newRow();
    
            for (String col : projection) {
                if (OpenableColumns.DISPLAY_NAME.equals(col)) {
                    b.add(getFileName(uri));
                } else if (OpenableColumns.SIZE.equals(col)) {
                    b.add(getDataLength(uri));
                } else if (OPENABLE_PROJECTION_DATA.equals(col)) {
                    b.add(getFileName(uri));
                } else {
                    b.add(null);
                }
            }
    
            return cursor;
        }
    
        @Override
        public String getType(Uri uri) {
            return URLConnection.guessContentTypeFromName(uri.toString());
        }
    
        protected String getFileName(Uri uri) {
            return uri.getLastPathSegment();
        }
    
        protected long getDataLength(Uri uri) {
            return AssetFileDescriptor.UNKNOWN_LENGTH;
        }
    
        @Override
        public Uri insert(Uri uri, ContentValues initialValues) {
            throw new RuntimeException("Operation not supported");
        }
    
        @Override
        public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
            throw new RuntimeException("Operation not supported");
        }
    
        @Override
        public int delete(Uri uri, String where, String[] whereArgs) {
            throw new RuntimeException("Operation not supported");
        }
    }
    
    /**
     * This class extends the AbstractFileProvider
     */
    public class FileProvider extends AbstractFileProvider {
    
        public static final String CONTENT_URI = "content://";
        private File baseDir;
    
        @Override
        public boolean onCreate() {
            baseDir = getContext().getCacheDir();
    
            if (baseDir != null && baseDir.exists()) {
                return true;
            }
    
            Log.e("FileProvider", "Can't access cache directory");
            return false;
        }
    
        @Override
        public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
            File f = new File(baseDir, uri.getPath());
    
            if (f.exists()) {
                return ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
            }
    
            throw new FileNotFoundException(uri.getPath());
        }
    
        @Override
        protected long getDataLength(Uri uri) {
            File f = new File(baseDir, uri.getPath());
    
            return f.length();
        }
    
        public static Uri getUriForFile(String authority, File file) {
            return Uri.parse(CONTENT_URI + authority + "/" + file.getName());
        }
    }
    



    -------------编辑:05/11/16--------------
    添加了对多张图片的支持:

    1. 复制baseDir文件夹中的所有图片
    2. FileProvider 中实现delete() 方法
    3. 使用startActivityForResult
    4. 收听onActivityResult
    5. 现在您可以删除所有临时图像

    对于电子邮件附件,您必须等待发送电子邮件才能删除文件,否则您将发送一个空附件

    【讨论】:

    • 所以你没有使用带有路径的xml?我在放置多条路径时遇到问题。一条路径有效,当我放两条时它不起作用
    • 如何处理其他应用共享的文件?
    猜你喜欢
    • 1970-01-01
    • 2013-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-09
    • 1970-01-01
    • 2013-11-08
    • 1970-01-01
    相关资源
    最近更新 更多