2017-02-15 41 views
16

Я пытаюсь сделать кнопку, открывающую камеру и сфотографирую. мой код здесьandroid.os.FileUriExposedException: file.jpg выставляется за пределы приложения через ClipData.Item.getUri()

//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; 

У меня тоже проблема с startActivityForResult в методе OnClick и импорт java.text.SimpleDateFormat; также прыгать во время выполнения исключения я работаю с buildtoolsversion SDK 25

+3

'Uri.fromFile()' не будет работать на Android 7.0+ с 'targetSdkVersion' 24 или выше. Вместо этого используйте 'FileProvider', как показано в [этом примере приложения] (https://github.com/commonsguy/cw-omnibus/tree/master/Camera/FileProvider). См. Также [это сообщение в блоге] (https://commonsware.com/blog/2016/03/14/psa-file-scheme-ban-n-developer-preview.html) и [это сообщение в блоге] (https://commonsware.com/blog/2016/08/31/granting-permissions-uri-intent-extra.html). – CommonsWare

+0

См. Ответ в этом вопросе: http://stackoverflow.com/questions/38200282/android-os-fileuriexposedexception-file-storage-emulated-0-test-txt-exposed –

+1

Легкое объяснение доступно здесь: [link] (https://inthecheesefactory.com/blog/how-to-share-access-to-file-with-fileprovider-on-android-nougat/en) – Meet

ответ

51

Кроме решения с использованием FileProvider, есть еще один способ обойти это. Проще говоря

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); 
StrictMode.setVmPolicy(builder.build()); 

in Application.onCreate() method. Таким образом, VM игнорирует экспозицию URI файла.

+2

Это работало для меня, спасибо, все, что я должен знать о возможном будущем проблема, или я хорошо на какое-то время? –

+0

работает отлично, спасибо rahul – RamBen

+0

Спасибо за отличный ответ. –

6

Эта информация от: FileUriExposedException

Это только брошено для приложений, ориентированных на N или выше. Приложения, ориентированные на более ранние версии SDK, могут делиться файлом: // Uri, но это настоятельно не рекомендуется.

Так что, если ваш файл app/build.gradlecompileSdkVersion (цель сборки) является Android N (уровень 24 API) или выше, эта ошибка возникает, если вы пишете файл, который может возможно получить доступ к другим приложениям. Самое главное, что здесь, как вы должны сделать это движение вперед:

взято отсюда: FileProvider

Изменение:

return Uri.fromFile(mediaFile); 

быть

return FileProvider.getUriForFile(getApplicationContext(), getPackageName()+".fileprovider", mediaFile); 

Примечание: если вы управляете 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> 

Затем вам нужно добавить файл с именем filepaths.xml в свой app/src/main/res/xml каталог со следующим содержимым

<paths> 
    <external-files-path name="Pictures" path="Pictures" /> 
</paths> 

Примечание: для кого-либо еще, мы использовали external-files-path выше, поскольку Омер использовал getExternalFilesDir(Environment.DIRECTORY_PICTURES) в коде. Для любого другого места, пожалуйста, проверьте FileProvider в разделе «Задание доступных файлов» и изменить external-files-path к одному из следующих основываясь на том, где файлы расположены:

files-path 
cache-path 
external-path 
external-files-path 
external-cache-path 

Кроме того, пересмотр Pictures выше будет ваше имя папки.

Одним из важных антишаблоном, чтобы избежать это, вы должны НЕ использование if (Build.VERSION.SDK_INT < 24) {, чтобы позволить вам сделать это по-старому, потому что это не их Android версии, что требует этого, это ваш сборки версия (Я пропустил это в первый раз, когда я его закодировал).

Я знаю, что это больше работает, однако это позволяет Android разрешать только другое приложение, с которым вы делитесь, временный доступ к файлу, который более безопасен для конфиденциальности пользователя.

+0

Простое редактирование: путь = "картинки" чувствителен к регистру. При использовании Environment.DIRECTORY_PICTURES с моей стороны мне пришлось использовать его как путь = «Картинки», иначе он не смог бы определить путь. –

+0

@DaveSanders Спасибо за то, что заметили, что я обновил свой ответ, чтобы отразить случай Environment.DIRECTORY_PICTURES, который оценивается как «Картинки» (Title case) – Jared

 Смежные вопросы

  • Нет связанных вопросов^_^