Android 如何将相机结果作为数据文件夹中的 uri 获取?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10042695/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to get camera result as a uri in data folder?
提问by Dharmendra
I am creating an application in which I want to capture a image and then I want to send that image in the email as a attachment.
我正在创建一个应用程序,我想在其中捕获图像,然后我想将该图像作为附件发送到电子邮件中。
I am opening a camera using android.provider.MediaStore.ACTION_IMAGE_CAPTURE
intent action and I am passing the Uri of the file as a parameter EXTRA_OUTPUT
to get the image back to the file. This is working perfectly and I am able to get the captured image if I use the external storage uri
as a EXTRA_OUTPUT
but if I use the data folder uri it is not working and the camera is not closing and its all buttons are not working.
我正在使用android.provider.MediaStore.ACTION_IMAGE_CAPTURE
意图操作打开相机,并将文件的 Uri 作为参数传递,EXTRA_OUTPUT
以将图像返回到文件中。这是完美的工作,如果我将其external storage uri
用作 a ,我能够获取捕获的图像, EXTRA_OUTPUT
但是如果我使用数据文件夹 uri,则它无法正常工作,并且相机未关闭且其所有按钮均无法正常工作。
Here is my code for get the result in the external storage directory
这是我在外部存储目录中获取结果的代码
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
File out = Environment.getExternalStorageDirectory();
out = new File(out, imagename);
i.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(out));
startActivityForResult(i, CAMERA_RESULT);
And this code is for get the image in the data folder
此代码用于获取数据文件夹中的图像
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
File out = getFilesDir();
out = new File(out, MyPharmacyOptions.PRESCRIPTION_IMAGE_NAME);
i.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(out));
startActivityForResult(i, CAMERA_RESULT);
I knew that the data folder is not accessible to third applicationso may be this causes an issue so I have create one content provider to share the file.
我知道第三个应用程序无法访问数据文件夹,所以这可能会导致问题,所以我创建了一个内容提供程序来共享文件。
Here is my content provide class
这是我的内容提供类
public class MyContentProvider extends ContentProvider {
private static final String Tag = RingtonContentProvider.class.getName();
public static final Uri CONTENT_URI = Uri
.parse("content://x.y.z/");
private static final HashMap<String, String> MIME_TYPES = new HashMap<String, String>();
static {
MIME_TYPES.put(".mp3", "audio/mp3");
MIME_TYPES.put(".wav", "audio/mp3");
MIME_TYPES.put(".jpg", "image/jpeg");
}
@Override
public boolean onCreate() {
return true;
}
@Override
public String getType(Uri uri) {
String path = uri.toString();
for (String extension : MIME_TYPES.keySet()) {
if (path.endsWith(extension)) {
return (MIME_TYPES.get(extension));
}
}
return (null);
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
File f = new File(getContext().getFilesDir(), uri.getPath());
if (f.exists()) {
return (ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY));
}
throw new FileNotFoundException(uri.getPath());
}
@Override
public Cursor query(Uri url, String[] projection, String selection,
String[] selectionArgs, String sort) {
throw new RuntimeException("Operation not supported");
}
@Override
public Uri insert(Uri uri, ContentValues initialValues) {
File file = new File(getContext().getFilesDir(), uri.getPath());
if(file.exists()) file.delete();
try {
file.createNewFile();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return Uri.fromFile(file);
}
@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) {
File f = new File(getContext().getFilesDir(), "image1.jpg");
if(f.exists()) f.delete();
f = new File(getContext().getFilesDir(), "image2.jpg");
if(f.exists()) f.delete();
getContext().getContentResolver().notifyChange(CONTENT_URI, null);
}
}
So to use this content provide I am using following code to pass the uri to the camera activity
因此,要使用此内容,我将使用以下代码将 uri 传递给相机活动
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
Uri uri = MyContentProvider.CONTENT_URI;
uri = Uri.withAppendedPath(uri, imagename);
getContentResolver().insert(uri, null);
getContentResolver().notifyChange(RingtonContentProvider.CONTENT_URI, null);
Log.d(Tag, uri.toString());
i.putExtra(MediaStore.EXTRA_OUTPUT, uri);
startActivityForResult(i, CAMERA_RESULT);
Now if I pass the url other then external storage directory the camera is opening but it is not closing in emulator but in device the camera is going to closed but I am not getting the result.
现在,如果我传递 url 以外的外部存储目录,则相机正在打开,但它没有在模拟器中关闭,但在设备中,相机将关闭,但我没有得到结果。
I have declared this content provide in the manifest file
我已在清单文件中声明此内容提供
<provider
android:name=".contentproviders.MyContentProvider"
android:authorities="x.y.z" />
Also I have given the permission to write the external storageand also for use the camera.
此外,我已授予写入外部存储和使用相机的权限。
I am able to capture the image using the external storage but I want to store the image in the data directory instead of external storage because if the external storage in not available I want to capture the image and want to send mail.
我能够使用外部存储捕获图像,但我想将图像存储在数据目录而不是外部存储中,因为如果外部存储不可用,我想捕获图像并想发送邮件。
If I create content provide then I can also share my image to the email application.
如果我创建内容提供,那么我还可以将我的图像共享到电子邮件应用程序。
If we not provide the extras with the camera intent it will return the image as a byte[] in the activity result as a data extra but this is for the purpose of the thumbnail so I can't get the high resolution image using this way.
如果我们不提供相机意图的额外内容,它将在活动结果中将图像作为字节 [] 作为数据额外返回,但这是为了缩略图的目的,所以我无法使用这种方式获得高分辨率图像.
回答by Dharmendra
There are two ways to solve this problem.
有两种方法可以解决这个问题。
1. Save bitmap which you received from onActivityResult method
1. 保存您从 onActivityResult 方法收到的位图
You can start camera through intent to capture photo using below code
您可以使用以下代码通过意图启动相机以捕获照片
Intent cameraIntent=new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, CAMERA_REQUEST);
After capture photo, you will get bitmap in onActivityResult method
拍摄照片后,您将在 onActivityResult 方法中获取位图
if (requestCode == CAMERA_REQUEST) {
Bitmap photo = (Bitmap) data.getExtras().get("data");
}
Now you can simply save this bitmap to internal storage
现在您可以简单地将此位图保存到内部存储
Note: Here bitmap object consists of thumb image, it will not have a full resolution image
注意:这里的位图对象由拇指图像组成,它不会有全分辨率图像
2. Save bitmap directly to internal storage using content provider
2. 使用内容提供器将位图直接保存到内部存储
Here we will create content provider class to allow permission of local storage directory to camera activity
在这里,我们将创建内容提供程序类以允许本地存储目录对相机活动的权限
Sample provider example as per below
示例提供程序示例如下
public class MyFileContentProvider extends ContentProvider {
public static final Uri CONTENT_URI = Uri.parse
("content://com.example.camerademo/");
private static final HashMap<String, String> MIME_TYPES =
new HashMap<String, String>();
static {
MIME_TYPES.put(".jpg", "image/jpeg");
MIME_TYPES.put(".jpeg", "image/jpeg");
}
@Override
public boolean onCreate() {
try {
File mFile = new File(getContext().getFilesDir(), "newImage.jpg");
if(!mFile.exists()) {
mFile.createNewFile();
}
getContext().getContentResolver().notifyChange(CONTENT_URI, null);
return (true);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@Override
public String getType(Uri uri) {
String path = uri.toString();
for (String extension : MIME_TYPES.keySet()) {
if (path.endsWith(extension)) {
return (MIME_TYPES.get(extension));
}
}
return (null);
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
File f = new File(getContext().getFilesDir(), "newImage.jpg");
if (f.exists()) {
return (ParcelFileDescriptor.open(f,
ParcelFileDescriptor.MODE_READ_WRITE));
}
throw new FileNotFoundException(uri.getPath());
}
}
After that you can simply use the URI to pass to camera activity using the below code
之后,您可以使用以下代码简单地使用 URI 传递给相机活动
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
i.putExtra(MediaStore.EXTRA_OUTPUT, MyFileContentProvider.CONTENT_URI);
startActivityForResult(i, CAMERA_RESULT);
If you don't want to create your own provider then you can use FileProviderfrom support-library-v4. For detailed help you can look into this post
如果您不想创建自己的提供程序,则可以使用support-library-v4 中的FileProvider。有关详细帮助,您可以查看此帖子
回答by Harry
Best solution I found is: FileProvider (needs support-library-v4) It uses the internal storage! https://developer.android.com/reference/android/support/v4/content/FileProvider.html
我找到的最佳解决方案是:FileProvider(需要 support-library-v4)它使用内部存储! https://developer.android.com/reference/android/support/v4/content/FileProvider.html
Define your FileProvider in Manifest in Application element:
<provider android:name="android.support.v4.content.FileProvider" android:authorities="your.package.name.fileprovider" android:exported="false" android:grantUriPermissions="true" > <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/image_path" /> </provider>
Add permissions in manifest root element:
<uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" android:required="false" />
Define your image paths in for example res/xml/image_path.xml:
<paths xmlns:android="http://schemas.android.com/apk/res/android"> <files-path name="captured_image" path="your/path/"/> </paths>
Java:
private static final int IMAGE_REQUEST_CODE = 1; // your authority, must be the same as in your manifest file private static final String CAPTURE_IMAGE_FILE_PROVIDER = "your.package.name.fileprovider";
在 Application 元素的 Manifest 中定义您的 FileProvider:
<provider android:name="android.support.v4.content.FileProvider" android:authorities="your.package.name.fileprovider" android:exported="false" android:grantUriPermissions="true" > <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/image_path" /> </provider>
在清单根元素中添加权限:
<uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" android:required="false" />
在例如 res/xml/image_path.xml 中定义您的图像路径:
<paths xmlns:android="http://schemas.android.com/apk/res/android"> <files-path name="captured_image" path="your/path/"/> </paths>
爪哇:
private static final int IMAGE_REQUEST_CODE = 1; // your authority, must be the same as in your manifest file private static final String CAPTURE_IMAGE_FILE_PROVIDER = "your.package.name.fileprovider";
4.1 capture intent:
4.1 捕获意图:
File path = new File(activity.getFilesDir(), "your/path");
if (!path.exists()) path.mkdirs();
File image = new File(path, "image.jpg");
Uri imageUri = FileProvider.getUriForFile(activity, CAPTURE_IMAGE_FILE_PROVIDER, image);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, IMAGE_REQUEST_CODE);
4.2 onActivityResult():
4.2 onActivityResult():
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == IMAGE_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
File path = new File(getFilesDir(), "your/path");
if (!path.exists()) path.mkdirs();
File imageFile = new File(path, "image.jpg");
// use imageFile to open your image
}
}
super.onActivityResult(requestCode, resultCode, intent);
}
回答by MKJParekh
Intent to call to capture photo,
意图打电话拍照,
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, CAMERA_REQUEST);
Then Take Bitmap in ActivityResult
然后将位图放入 ActivityResult
if (requestCode == CAMERA_REQUEST) {
Bitmap photo = (Bitmap) data.getExtras().get("data");
}
Then Write that Into Internal Memory, see this
然后将其写入内部存储器,请参阅
// The openfileOutput() method creates a file on the phone/internal storage in the context of your application
final FileOutputStream fos = openFileOutput("my_new_image.jpg", Context.MODE_PRIVATE);
// Use the compress method on the BitMap object to write image to the OutputStream
bm.compress(CompressFormat.JPEG, 90, fos);
Then next time to read that file,
然后下次读取该文件时,
Bitmap bitmap = BitmapFactory.decodeFile(file);
回答by Suvam Roy
At first save came photo in your external storage and try it -
首先将照片保存在您的外部存储中并尝试 -
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.imageView = (ImageView)this.findViewById(R.id.imageView1);
Button photoButton = (Button) this.findViewById(R.id.button1);
photoButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, CAMERA_REQUEST);
}
});
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CAMERA_REQUEST) {
Bitmap bmp = intent.getExtras().get("data");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray(); // convert camera photo to byte array
// save it in your external storage.
FileOutputStream fo = new FileOutputStream(new File(Environment.getExternalStorageDirectory() + "/_camera.png"));
fo.write(byteArray);
fo.flush();
fo.close();
}
}
Next target -
下一个目标——
File cameraFile = new File(Environment.getExternalStorageDirectory() + "/_camera.png");
startActivityForResult(Intent.createChooser(new Intent(Intent.ACTION_SEND)
.setType("image/jpg")
.putExtra(Intent.EXTRA_SUBJECT, "Subject")
.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(cameraFile))
.putExtra(Intent.EXTRA_TEXT, textBody), "Send your message using"), Constant.EMAIL);
回答by Droid Teahouse
You can also do this without needing a content provider since you will need the sd card to open the camera image capture intent anyway. You can of course hack around the presence of the sd card but not with the camera intent capture....So you are checking for external storage but need it at this point.... FYI you should also check out a crop library like crop-image instead of using native, as it is not well supported across devices.
您也可以在不需要内容提供程序的情况下执行此操作,因为无论如何您都需要 SD 卡来打开相机图像捕获意图。您当然可以绕过 SD 卡的存在,但不能使用相机意图捕获....所以您正在检查外部存储,但此时需要它....仅供参考,您还应该查看一个裁剪库,如裁剪图像而不是使用本机,因为它在设备之间没有得到很好的支持。
File mediaStorageDir;
String photoFileName = "photo.jpg";
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// page = getArguments().getInt("someInt", 0);
// title = getArguments().getString("someTitle");
// Get safe storage directory for photos
mediaStorageDir = new File(
Environment .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
APP_TAG);
// Create the storage directory if it does not exist
if (!mediaStorageDir.exists() && !mediaStorageDir.mkdirs()) {
Log.d(APP_TAG, "Directory exists: " + mediaStorageDir.isDirectory());
Log.d(APP_TAG, "Directory exists: " + mediaStorageDir.getPath());
Log.d(APP_TAG,
"Directory exists: "
+ Environment.getExternalStorageState());
Log.d(APP_TAG, "failed to create directory");
}
}
in your 'take the pic' code:
在您的“拍照”代码中:
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, getPhotoFileUri(photoFileName));
...
...
public Uri getPhotoFileUri(String fileName) {
return Uri.fromFile(new File(mediaStorageDir.getPath() + File.separator
+ fileName));
}
回答by kc ochibili
After researching this bug for some time, I have noticed that the activity that called the camera intent is only restarted when the phone is running out of memory. so because the activity is restarted, the object holding the path or Uri to the captured image is refreshed (nulled)
在研究这个错误一段时间后,我注意到调用相机意图的活动只有在手机内存不足时才会重新启动。因此,由于活动重新启动,保存捕获图像的路径或 Uri 的对象被刷新(归零)
so I would suggest you catch/ detect the null object in the onActivityResult, and prompt the user to free up space on their device or restart their phone for a quick temporary fix.
所以我建议您在 onActivityResult 中捕获/检测空对象,并提示用户释放设备上的空间或重新启动手机以进行快速临时修复。