拍摄照片并以全尺寸保存

Nor*_*her 1 android

我正在开发一个应用程序,它必须从相机拍摄照片并将其上传到 FireBase 数据库。但是,当我创建临时文件并尝试将图像从存储获取到 ImageView 时,出现错误:

2022-05-18 11:14:18.596 24790-24790/com.example.apptonia D/异常:/storage/emulated/0/Pictures/.temp/picture8489350187277543939.jpg:打开失败:EACCES(权限被拒绝)

我一直在尝试解决按照其他类似问题中的建议向清单添加多个权限的问题,并向活动添加代码。然而,它不起作用。

这是我的活动代码:

...


import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import android.Manifest;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.StrictMode;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.firebase.storage.FirebaseStorage;
import com.google.firebase.storage.OnProgressListener;
import com.google.firebase.storage.StorageReference;
import com.google.firebase.storage.UploadTask;

import java.io.ByteArrayOutputStream;
import java.io.File;

public class TakePhotoActivity extends AppCompatActivity {

    private ImageView image;
    private Button takePhotoButton, guardarButton;
    private Bitmap photoTaken;
    private Uri mImageUri;
    static final int REQUEST_IMAGE_CAPTURE = 1;
    private static final int REQUEST_EXTERNAL_STORAGE = 1;
    private static String[] PERMISSIONS_STORAGE = {
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE};


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_take_photo);

        guardarButton = findViewById(R.id.buttonGuardar);
        image = findViewById(R.id.image);
        takePhotoButton = findViewById(R.id.takePhotoButton);

        takePhotoButton.setOnClickListener(v -> {
            Log.d("LOG", "CLICK BUTTON");
            dispatchTakePictureIntent();
        });

        FirebaseStorage storage = FirebaseStorage.getInstance();


        guardarButton.setOnClickListener(v -> {
            StorageReference storageRef = storage.getReference("/" + StaticUser.mail + "/" +
                    StaticUser.year + "/" + StaticUser.getTrimestre() + "/" + StaticUser.tipo + "/" +
                    StaticUser.getFecha() + StaticUser.getRandomString(40));


            //image.setDrawingCacheEnabled(true);
            //image.buildDrawingCache();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            photoTaken.compress(Bitmap.CompressFormat.JPEG, 80, baos);
            byte[] data = baos.toByteArray();

            UploadTask uploadTask = storageRef.putBytes(data);


            uploadTask.addOnFailureListener(exception -> Toast.makeText(getApplicationContext(),
                    "Subida incorrecta", Toast.LENGTH_LONG).show());

            uploadTask.addOnSuccessListener(taskSnapshot -> {
                // taskSnapshot.getMetadata() contains file metadata such as size, content-type, etc.
                // ...
                Toast.makeText(getApplicationContext(),
                        "Subida correcta!", Toast.LENGTH_LONG).show();


                Intent intent = new Intent(this, WelcomeActivity.class);
                startActivity(intent);
                finish();
            });

            uploadTask.addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() {
                @Override
                public void onProgress(UploadTask.TaskSnapshot taskSnapshot) {
                    double progress = (100.0 * taskSnapshot.getBytesTransferred()) / taskSnapshot.getTotalByteCount();
                    Toast.makeText(getApplicationContext(),
                            "Subiendo foto... " + progress + "%", Toast.LENGTH_SHORT).show();
                }
            });
        });
    }

    private void dispatchTakePictureIntent() {
        /*
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
        }
         */
        try
        {
            verifyStoragePermissions(this);
            StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
            StrictMode.setVmPolicy(builder.build());
            // place where to store camera taken picture
            Log.d("LOG", "CREATING PHOTO INTENT");
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            File photo;
            Log.d("LOG", "CREATING TEMPORARY FILE");
            photo = this.createTemporaryFile("picture", ".jpg");
            Log.d("LOG", "DELETING TEMPORARY FILE");
            photo.delete();
            mImageUri = Uri.fromFile(photo);
            Log.d("LOG", "PUTTING EXTRA IN MEDIASTORE");
            intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
            if (intent.resolveActivity(getPackageManager()) != null) {
                startActivityForResult(intent, REQUEST_IMAGE_CAPTURE);
            }
        }
        catch(Exception e)
        {
            Log.d("Error", e.getMessage());
            Toast.makeText(getApplicationContext(), "Please check SD card! Image shot is impossible!",
                    Toast.LENGTH_SHORT);
        }
    }

    private File createTemporaryFile(String part, String ext) throws Exception
    {
        Log.d("LOG", "getting tempDir");
        File tempDir= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        tempDir = new File(tempDir.getAbsolutePath()+"/.temp/");
        Log.d("LOG", tempDir.getAbsolutePath()+"/.temp/");
        if(!tempDir.exists())
        {
            Log.d("LOG", "creating dir");
            tempDir.mkdirs();
        }
        Log.d("LOG", "returning new file created");
        return File.createTempFile(part, ext, tempDir);
    }

    public void grabImage(ImageView imageView) {
        this.getContentResolver().notifyChange(mImageUri, null);
        ContentResolver cr = this.getContentResolver();
        Bitmap bitmap;
        try {
            bitmap = android.provider.MediaStore.Images.Media.getBitmap(cr, mImageUri);
            imageView.setImageBitmap(bitmap);
        } catch (Exception e) {
            Toast.makeText(this, "Failed to load", Toast.LENGTH_SHORT).show();
            Log.d("Exception", e.getMessage());
        }
    }

    public static void verifyStoragePermissions(Activity activity) {
        // Check if we have write permission
        int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);

        if (permission != PackageManager.PERMISSION_GRANTED) {
            // We don't have permission so prompt the user
            ActivityCompat.requestPermissions(
                    activity,
                    PERMISSIONS_STORAGE,
                    REQUEST_EXTERNAL_STORAGE
            );
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {

            try {
                ContentResolver cr = this.getContentResolver();
                Bitmap imageBitmap = android.provider.MediaStore.Images.Media.getBitmap(cr, mImageUri);
                this.grabImage(image);
                photoTaken = imageBitmap;
                image.setImageBitmap(imageBitmap);
            }
            catch (Exception e) {
                Toast.makeText(this, "Failed to load", Toast.LENGTH_SHORT).show();
                Log.d("Exception", e.getMessage());

            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

显现:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.apptonia">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="Manifest.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="Manifest.permission.READ_EXTERNAL_STORAGE" />

    <uses-feature
        android:name="android.hardware.camera"
        android:required="true" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:preserveLegacyExternalStorage="true"
        android:requestLegacyExternalStorage="true"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.AppTonia"
        android:usesCleartextTraffic="true"
        tools:targetApi="m">
        <activity
            android:name=".TakePhotoActivity"
            android:exported="false" />
        <activity
            android:name=".SelectFamilyActivity"
            android:exported="false" />
        <activity
            android:name=".MainActivity$SelectFamilyActivity"
            android:exported="false" />
        <activity
            android:name=".WelcomeActivity"
            android:exported="false"
            android:label="@string/title_activity_welcome"
            android:theme="@style/Theme.AppTonia.NoActionBar" />
        <activity
            android:name=".RegistroActivity"
            android:exported="false" />
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
Run Code Online (Sandbox Code Playgroud)

Abu*_*kar 5

让我解释一下整个过程。

首先,您必须在清单中声明权限,但您CAMERA现在没有应该存在的权限以及WRITE_EXTERNAL_STORAGE已经存在的权限。

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
Run Code Online (Sandbox Code Playgroud)

现在你必须像这样实时请求这些权限,PERMISSION_REQUEST_CODE可以是任何常量值:

    private void requestPermission() {

        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        if (requestCode == PERMISSION_REQUEST_CODE) {

            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this, "Permission granted successfully", Toast.LENGTH_LONG).show();
                showCameraGalleryBottomSheet();
            } else {
                Toast.makeText(this, "Permission denied", Toast.LENGTH_LONG).show();
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

您必须在调用我将在下面解释的方法requestPermission()之前调用。startCamera()

    private void startCamera() {

        ContentValues values = new ContentValues(1);
        values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
        fileUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        startActivityForResult(intent, CAPTURE_IMAGE);
    }
Run Code Online (Sandbox Code Playgroud)

这里CAPTURE_IMAGE可以是任何常量值,您可以在 中使用它来初始化并获取结果onActivityResult()

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

        if (requestCode == CAPTURE_IMAGE && resultCode == RESULT_OK) {
            processCapturedPhoto();
        }
    }
Run Code Online (Sandbox Code Playgroud)

要处理拍摄的照片,您可以使用以下方法:

    private void processCapturedPhoto() {

        try {
            String[] columns = {MediaStore.Images.ImageColumns.DATA};

            Cursor cursor = getContentResolver().query(Uri.parse(fileUri.toString()), columns, null, null, null);
            cursor.moveToFirst();

            String photoPath = cursor.getString(0);
            cursor.close();

            File file = new File(photoPath);
            uriImage = Uri.fromFile(file);

            Bitmap bitmap = null;

            try {
                bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uriImage);
            } catch (IOException e) {
                e.printStackTrace();
            }

            imageView.setImageBitmap(handleSamplingAndRotationBitmap(uriImage));

            uploadImageToFirebaseStorage();

        } catch (CursorIndexOutOfBoundsException | IOException ex) {
            ex.printStackTrace();

            String[] columns = {MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DATE_ADDED};
            Cursor cursor = getContentResolver().query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, columns, null, null, "${MediaStore.MediaColumns.DATE_ADDED} DESC");

            int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            cursor.moveToFirst();

            String photoPath = cursor.getString(columnIndex);
            cursor.close();

            File file = new File(photoPath);
            uriImage = Uri.fromFile(file);

            Bitmap bitmap = null;

            try {
                bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uriImage);
            } catch (IOException e) {
                e.printStackTrace();
            }

            imageView.setImageBitmap(bitmap);

            uploadImageToFirebaseStorage();
        }
    }
Run Code Online (Sandbox Code Playgroud)

您可以private Uri uriImage = null;全局初始化。您也可以Bitmap直接使用将其设置为ImageView,但可能存在一些旋转问题,因此为了处理这个问题,我使用下面定义的这些旋转采样辅助方法(这些方法将在内部自动调用,processCapturedPhoto()因此您不必专门调用它们):

    private Bitmap handleSamplingAndRotationBitmap(Uri selectedImage) throws IOException {
        int MAX_HEIGHT = 1024;
        int MAX_WIDTH = 1024;

        // First decode with inJustDecodeBounds=true to check dimensions
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        InputStream imageStream = getContentResolver().openInputStream(selectedImage);
        BitmapFactory.decodeStream(imageStream, null, options);
        imageStream.close();

        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, MAX_WIDTH, MAX_HEIGHT);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        imageStream = getContentResolver().openInputStream(selectedImage);
        Bitmap img = BitmapFactory.decodeStream(imageStream, null, options);
        if (img != null) img = rotateImageIfRequired(img, selectedImage);
        else {
            Toast.makeText(this, "Image not available", Toast.LENGTH_SHORT).show();
            return null;
        }
        return img;
    }

    private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        int height = options.outHeight;
        int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {
            int heightRatio = Math.round(((float) height / (float) reqHeight));
            int widthRatio = Math.round(((float) width / (float) reqWidth));
            inSampleSize = Math.min(heightRatio, widthRatio);
            float totalPixels = (float) (width * height);
            float totalReqPixelsCap = (float) (reqWidth * reqHeight * 2);
            while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
                inSampleSize++;
            }
        }
        return inSampleSize;
    }

    private Bitmap rotateImageIfRequired(Bitmap img, Uri selectedImage) throws IOException {
        ExifInterface ei = new ExifInterface(selectedImage.getPath());

        switch (ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)) {
            case ExifInterface.ORIENTATION_ROTATE_90:
                return rotateImage(img, 90);
            case ExifInterface.ORIENTATION_ROTATE_180:
                return rotateImage(img, 180);
            case ExifInterface.ORIENTATION_ROTATE_270:
                return rotateImage(img, 270);
            default:
                return img;
        }
    }

    private Bitmap rotateImage(Bitmap img, int degree) {
        Matrix matrix = new Matrix();
        matrix.postRotate((float) degree);
        Bitmap rotatedImg = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
        img.recycle();
        return rotatedImg;
    }
Run Code Online (Sandbox Code Playgroud)

旋转和采样后,uploadImageToFirebaseStorage()将调用该方法将图像上传到firebase。

    private void uploadImageToFirebaseStorage() {

        final StorageReference refProfileImage = FirebaseStorage.getInstance().getReference("Images/" + System.currentTimeMillis() + ".jpg");

        if (uriImage != null) {

            refProfileImage.putFile(uriImage).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
                @Override
                public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {

                    refProfileImage.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
                        @Override
                        public void onSuccess(Uri uri) {

                            urlImage = uri.toString();

                            Log.d("TAGOO", urlImage);
                        }
                    }).addOnFailureListener(new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception e) {

                            Toast.makeText(NotesActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
                        }
                    });
                }
            }).addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {

                    Toast.makeText(NotesActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
                }
            });

        } else {

            Toast.makeText(this, "Some error occurred while uploading to database", Toast.LENGTH_SHORT).show();
        }
    }
Run Code Online (Sandbox Code Playgroud)

urlImage可以是String全局定义的变量,用于获取上传的图像链接,您可以根据需要保存此链接。例如; 如果用户上传了他的个人资料图片,那么您可以将此图像链接保存在他的实时数据库节点中,以便稍后使用他的 UID 进行访问。

如果有不清楚的地方请告诉我:)