android.os.FileUriExposedException:file.jpg通过ClipData.Item.getUri()暴露在app之外

Ome*_*er 33 media camera android image onclick

我尝试按下一个打开相机并拍照的按钮.我的代码在这里

//for imports check on bottom of this code block

public class HomeProfileActivity extends AppCompatActivity {
//Button camera
public static final String TAG = HomeProfileActivity.class.getSimpleName();
public static final int REQUEST_TAKE_PHOTO = 0;
public static final int REQUEST_TAKE_VIDEO = 1;
public static final int REQUEST_PICK_PHOTO = 2;
public static final int REQUEST_PICK_VIDEO = 3;
public static final int MEDIA_TYPE_IMAGE = 4;
public static final int MEDIA_TYPE_VIDEO = 5;

private Uri mMediaUri;
private ImageView photobutton;
private Button buttonUploadImage, buttonTakeImage;

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_home_profile);
    ButterKnife.bind(this);
}

@OnClick(R.id.buttonTakeImage)
void takePhoto() {
    mMediaUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE);
    if (mMediaUri == null) {
        Toast.makeText(this,
                "There was a problem accessing your device's external storage.",
                Toast.LENGTH_LONG).show();
    }
    else {
        Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, mMediaUri);
        startActivityForResult(takePhotoIntent, REQUEST_TAKE_PHOTO);
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (resultCode == RESULT_OK){
        if (requestCode == REQUEST_TAKE_PHOTO) {
            Intent intent = new Intent(this, ViewImageActivity.class);
            intent.setData(mMediaUri);
            startActivity(intent);
        }
    }
    else if (resultCode != RESULT_CANCELED){
        Toast.makeText(this, "Sorry, there was an error", Toast.LENGTH_SHORT).show();
    }
}

private Uri getOutputMediaFileUri(int mediaType) {
    // check for external storage
    if (isExternalStorageAvailable()) {
        // get the URI

        // 1. Get the external storage directory
        File mediaStorageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);

        // 2. Create a unique file name
        String fileName = "";
        String fileType = "";
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());

        if (mediaType == MEDIA_TYPE_IMAGE) {
            fileName = "IMG_" + timeStamp;
            fileType = ".jpg";
        } else if (mediaType == MEDIA_TYPE_VIDEO) {
            fileName = "VID_" + timeStamp;
            fileType = ".mp4";
        } else {
            return null;
        }
        // 3. Create the file
        File mediaFile;
        try {
            mediaFile = File.createTempFile(fileName, fileType, mediaStorageDir);
            Log.i(TAG, "File: " + Uri.fromFile(mediaFile));

            // 4. Return the file's URI
            return Uri.fromFile(mediaFile);
        }
        catch (IOException e) {
                Log.e(TAG, "Error creating file: " +
                        mediaStorageDir.getAbsolutePath() + fileName + fileType);
            }
        }

        // something went wrong
        return null;
    }

private boolean isExternalStorageAvailable(){
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state)){
        return true;
    }
    else {
        return false;
    }
}

import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import butterknife.ButterKnife;
import butterknife.OnClick;
Run Code Online (Sandbox Code Playgroud)

startActivityForResult在方法onclick中也有问题,导入java.text.SimpleDateFormat;也跳转到异常运行时我正在使用buildtoolsversion sdk 25

小智 126

除了使用FileProvider的解决方案之外,还有另一种方法可以解决这个问题.简单的说

 StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
 StrictMode.setVmPolicy(builder.build());
Run Code Online (Sandbox Code Playgroud)

在Application.onCreate()方法中.通过这种方式,VM忽略文件URI暴露.

  • 这对我有用,谢谢,我应该知道的任何可能的未来问题或者我好一会儿? (3认同)
  • StrictMode 默认是禁用的,用户需要在他们的 Android 中输入“开发者模式”才能启用它。这使得使用 StrictMode 的解决方案无关紧要。 (3认同)

Jar*_*red 29

此信息来自: FileUriExposedException

仅针对N或更高的应用程序抛出此操作.定位早期SDK版本的应用程序可以共享file:// Uri,但强烈建议不要这样做.

因此,如果您的app/build.gradle文件compileSdkVersion(构建目标)是Android N(API级别24)或更高,如果您编写的文件可能被其他应用程序访问,则会引发此错误.最重要的是,这是你应该如何做到这一点:

取自这里: FileProvider

更改:

return Uri.fromFile(mediaFile);
Run Code Online (Sandbox Code Playgroud)

成为

return FileProvider.getUriForFile(getApplicationContext(), getPackageName()+".fileprovider", mediaFile);
Run Code Online (Sandbox Code Playgroud)

注意:如果你控制mydomain.com,你也可以更换getPackageName()+".fileprovider""com.mydomain.fileprovider"(同去为你的AndroidManifest.xml下面.

您还需要AndroidManifest.xml</application>标记之前将以下内容添加到文件中

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="${applicationId}.fileprovider"
        android:grantUriPermissions="true"
        android:exported="false">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/filepaths" />
    </provider>
Run Code Online (Sandbox Code Playgroud)

然后,您需要使用以下内容添加名为filepaths.xml您的app/src/main/res/xml目录的文件

<paths>
    <external-files-path name="Pictures" path="Pictures" />
</paths>
Run Code Online (Sandbox Code Playgroud)

注意:对于其他任何人,我们在external-files-path上面使用getExternalFilesDir(Environment.DIRECTORY_PICTURES)过,因为代码中使用了Omer .对于任何其他位置,请检查"指定可用文件"部分下的FileProvider,并根据文件所在位置更改external-files-path为以下位置之一:

files-path
cache-path
external-path
external-files-path
external-cache-path
Run Code Online (Sandbox Code Playgroud)

另外,修改Pictures上面的文件夹名称.

一个重要的反模式避免的是,你应该使用if (Build.VERSION.SDK_INT < 24) {,允许你做旧的方式,因为这不是他们的Android版本,需要这个,这是你的内部版本(我错过了这个我第一次编码的话).

我知道这是更多的工作,但是,这允许Android只允许您共享的其他应用程序,临时访问该文件,这对用户的隐私更安全.

  • 请注意,`FileProvider` 的 androidX 版本有包 `androidx.core.content.FileProvider` (3认同)
  • 只需快速编辑:path="pictures" 区分大小写。当我使用 Environment.DIRECTORY_PICTURES 时,我必须将其大写为 path="Pictures",否则它无法找出路径。 (2认同)