简体   繁体   English

将图像添加到媒体库 - Android

[英]Add image to Media Gallery - Android

I am trying to add a new image to the gallery.我正在尝试向图库添加新图像。 I pick an already existing image though an intent and then resize and compress it.我通过一个意图选择一个已经存在的图像,然后调整大小并压缩它。

Then I store the resulting bitmap:然后我存储生成的位图:

public static File compressAndSaveImage(Context ctx, Uri imageUri) throws FileNotFoundException {
    File file = null;

    if (imageUri != null) {
        ContextWrapper cw = new ContextWrapper(ctx);
        File directory = cw.getDir("imageDir", Context.MODE_PRIVATE);
        file = new File(directory, imageUri.getLastPathSegment());
        System.out.println("storing to " + file);

        InputStream input = ctx.getContentResolver().openInputStream(imageUri);
        Bitmap b = ImageManager.resize(BitmapFactory.decodeStream(input),
                ctx.getResources().getDimension(R.dimen.player_thumb_w),
                ctx.getResources().getDimension(R.dimen.player_thumb_h));

        FileOutputStream fos = new FileOutputStream(file);
        if (b.compress(Bitmap.CompressFormat.PNG, 100, fos)) {
            System.out.println("Compression success");// bmp is your Bitmap instance
        }
        addPictureToGallery(ctx, file);
    }
    return file;
}

But when I try to add the image to the gallery, I get no errors and the image is not added.但是当我尝试将图像添加到图库时,我没有收到任何错误并且没有添加图像。 I have tried both the methods below:我已经尝试了以下两种方法:

private static void addPictureToGallery(Context ctx, File filepath) {
//        Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
//        System.out.println("Publish: " + filepath.exists());
//        System.out.println("Publish: " + filepath.getAbsolutePath());
//        Uri contentUri = Uri.fromFile(filepath);
//        mediaScanIntent.setData(contentUri);
//        ctx.sendBroadcast(mediaScanIntent);
    MediaScannerConnection.scanFile(
            ctx,
            new String[]{filepath.getAbsolutePath()},
            null,
            new MediaScannerConnection.OnScanCompletedListener() {
                @Override
                public void onScanCompleted(String path, Uri uri) {
                    Log.w("mydebug", "file " + path + " was scanned successfully: " + uri);
                }
            });
    }
}

The callback prints the following line:回调打印以下行:

file /data/data/test.myapps.appname/app_imageDir/6045564126748266738 was scanned successfully: content://media/external/file/7838

What am I missing?我错过了什么?

Thanks @zgc7009, you set me on the right track.谢谢@zgc7009,你让我走上了正确的轨道。 I used some of your code, and this is the end result for anyone else wanting to solve.我使用了您的一些代码,这是其他任何想要解决的人的最终结果。

The problem indeed was that I was storing the image in my application's local storage.问题确实是我将图像存储在应用程序的本地存储中。

File storedImagePath = generateImagePath("player", "png");
if (!compressAndSaveImage(storedImagePath, bitmap)) {
    return null;
}
Uri url = addImageToGallery(context.getContentResolver(), "png", storedImagePath);

Where the three methods used are:其中使用的三种方法是:

Generate Path生成路径

private static File getImagesDirectory() {
    File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + File.separator + IMAGE_DIR);//Environment.getExternalStorageDirectory()
    if (!file.mkdirs() && !file.isDirectory()) {
        Log.e("mkdir", "Directory not created");
    }
    return file;
}

public static File generateImagePath(String title, String imgType) {
    SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy-hh-mm-ss");
    return new File(getImagesDirectory(), title + "_" + sdf.format(new Date()) + "." + imgType);
}

Compress And Save压缩并保存

public boolean compressAndSaveImage(File file, Bitmap bitmap) {
    boolean result = false;
    try {
        FileOutputStream fos = new FileOutputStream(file);
        if (result = bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)) {
            Log.w("image manager", "Compression success");
        }
        fos.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return result;
}

Add To Gallery添加到图库

public Uri addImageToGallery(ContentResolver cr, String imgType, File filepath) {
    ContentValues values = new ContentValues();
    values.put(MediaStore.Images.Media.TITLE, "player");
    values.put(MediaStore.Images.Media.DISPLAY_NAME, "player");
    values.put(MediaStore.Images.Media.DESCRIPTION, "");
    values.put(MediaStore.Images.Media.MIME_TYPE, "image/" + imgType);
    values.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis());
    values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
    values.put(MediaStore.Images.Media.DATA, filepath.toString());

    return cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
}

I would put this as a comment as I don't have time to really go into detail about things, but it's obviously too long.我会把它作为评论,因为我没有时间真正详细介绍事情,但这显然太长了。 Try something like this (I use this for storing bitmaps to the gallery).尝试这样的事情(我用它来将位图存储到画廊)。 As a note, it doesn't seem you are doing anything but scanning an image.请注意,除了扫描图像之外,您似乎没有做任何事情。 Right now it seems you are storing the file to your internal storage, not your phones content (gallery).现在,您似乎将文件存储到内部存储器,而不是手机内容(图库)。

Assume we are running this from ActivityA.class that extends Activity假设我们从 ActivityA.class 运行它,它扩展了 Activity

 /**
 * Will save our card (currently set as a bitmap) as a jpeg and save it into our devices
 * native photo gallery. Unfortunately, the default method for this will add the image to the bottom
 * of the gallery by default, that is no good. This more complex method that utilizes Android's
 * native methods will allow us to store the bitmap at the top of the gallery by setting it's
 * meta data to today's date.
 */
public class SaveBitmapToDevice extends AsyncTask<Bitmap, Void, String>{

    private final ShareType type;

    public SaveBitmapToDevice(ShareType type){
        this.type = type;
    }

    @Override
    protected String doInBackground(Bitmap... cards) {
        return insertImageIntoGallery(getContentResolver(), cards[0], getString(R.string.card_gallery_title),
                getString(R.string.card_gallery_label));
    }

    /**
     * A copy of the Android internals insertImage method, this method populates the
     * meta data with DATE_ADDED and DATE_TAKEN. This fixes a common problem where media
     * that is inserted manually gets saved at the end of the gallery (because date is not populated).
     * @see android.provider.MediaStore.Images.Media#insertImage(android.content.ContentResolver, Bitmap, String, String).
     * If the MediaStore not available, we will redirect the file to our alternative source, the SD card.
     */
    public String insertImageIntoGallery(ContentResolver cr, Bitmap source, String title, String description) {

        ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.TITLE, title);
        values.put(MediaStore.Images.Media.DISPLAY_NAME, title);
        values.put(MediaStore.Images.Media.DESCRIPTION, description);
        values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
        // Add the date meta data to ensure the image is added at the front of the gallery
        values.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis());
        values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());

        Uri url = null;
        String stringUrl = null;    /* value to be returned */

        try {
            url = cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

            if (source != null) {
                OutputStream imageOut = cr.openOutputStream(url);
                try {
                    source.compress(Bitmap.CompressFormat.JPEG, 50, imageOut);
                } finally {
                    imageOut.close();
                }

                long id = ContentUris.parseId(url);
                // Wait until MINI_KIND thumbnail is generated.
                Bitmap miniThumb = MediaStore.Images.Thumbnails.getThumbnail(cr, id, MediaStore.Images.Thumbnails.MINI_KIND, null);
                // This is for backward compatibility.
                storeThumbnail(cr, miniThumb, id, 50F, 50F, MediaStore.Images.Thumbnails.MICRO_KIND);
            } else {
                cr.delete(url, null, null);
                return storeToAlternateSd(source, title);
                // url = null;
            }
        } catch (Exception e) {
            if (url != null) {
                cr.delete(url, null, null);
                return storeToAlternateSd(source, title);
                // url = null;
            }
        }

        savedOnSD = false;
        if (url != null) {
            stringUrl = url.toString();
        }

        return stringUrl;
    }

    /**
     * A copy of the Android internals StoreThumbnail method, it used with the insertImage to
     * populate the android.provider.MediaStore.Images.Media#insertImage with all the correct
     * meta data. The StoreThumbnail method is private so it must be duplicated here.
     * @see android.provider.MediaStore.Images.Media (StoreThumbnail private method).
     */
    private Bitmap storeThumbnail(
            ContentResolver cr,
            Bitmap source,
            long id,
            float width,
            float height,
            int kind) {

        // create the matrix to scale it
        Matrix matrix = new Matrix();

        float scaleX = width / source.getWidth();
        float scaleY = height / source.getHeight();

        matrix.setScale(scaleX, scaleY);

        Bitmap thumb = Bitmap.createBitmap(source, 0, 0,
                source.getWidth(),
                source.getHeight(), matrix,
                true
        );

        ContentValues values = new ContentValues(4);
        values.put(MediaStore.Images.Thumbnails.KIND,kind);
        values.put(MediaStore.Images.Thumbnails.IMAGE_ID,(int)id);
        values.put(MediaStore.Images.Thumbnails.HEIGHT,thumb.getHeight());
        values.put(MediaStore.Images.Thumbnails.WIDTH,thumb.getWidth());

        Uri url = cr.insert(MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI, values);

        try {
            OutputStream thumbOut = cr.openOutputStream(url);
            thumb.compress(Bitmap.CompressFormat.JPEG, 100, thumbOut);
            thumbOut.close();
            return thumb;
        } catch (FileNotFoundException ex) {
            Log.e("IMAGE_COMPRESSION_ERROR", "File not found");
            ex.printStackTrace();
            return null;
        } catch (IOException ex) {
            Log.e("IMAGE_COMPRESSION_ERROR", "IO Exception");
            ex.printStackTrace();
            return null;
        }
    }

    /**
     * If we have issues saving into our MediaStore, save it directly to our SD card. We can then interact with this file
     * directly, opposed to pulling from the MediaStore. Again, this is a backup method if things don't work out as we
     * would expect (seeing as most devices will have a MediaStore).
     *
     * @param src
     * @param title
     * @return - the file's path
     */
    private String storeToAlternateSd(Bitmap src, String title){
        if(src == null)
            return null;

        File sdCardDirectory = new File(Environment.getExternalStorageDirectory() + File.separator + "My Cards");
        if(!sdCardDirectory.exists())
            sdCardDirectory.mkdir();

        SimpleDateFormat sdf = new SimpleDateFormat("MM-dd-yyyy - (hh.mm.a)", Locale.US);
        File image = new File(sdCardDirectory, title + " -- [" + sdf.format(new Date()) + "].jpg");
        try {
            FileOutputStream imageOut = new FileOutputStream(image);
            src.compress(Bitmap.CompressFormat.JPEG, 100, imageOut);
            imageOut.close();
            savedOnSD = true;
            return image.getAbsolutePath();
        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
            return null;
        } catch (IOException ex) {
            ex.printStackTrace();
            return null;
        }
    }

    @Override
    public void onPostExecute(String url){
        if(url != null){
            Intent intent = new Intent();
            intent.setAction(Intent.ACTION_VIEW);
            if(savedOnSD){
                File file = new File(url);
                if(file.exists())
                    intent.setDataAndType(Uri.fromFile(file), "image/jpeg");
                else
                    return;
            }
            else
                intent.setDataAndType(Uri.parse(url), "image/jpeg");

            ActivityA.this.startActivity(intent);
        }
        else
            Toast.makeText(ActivityA.this, getString(R.string.error_compressing), Toast.LENGTH_SHORT).show();
    }

}

You could also use MediaStore's static methods...您还可以使用 MediaStore 的静态方法...

public String addImageToGallery(ContentResolver cr, File filepath) {
    try {
       return MediaStore.Images.Media.insertImage(cr, filepath.toString(), 
                                                  filepath.getName(), "Image Description");
    } catch (FileNotFoundException e) {
       e.printStackTrace();
       return e.getMessage();
    }
}

The String returned from insertImage is the same as calling toString() on the Uri object in the accepted answer.insertImage返回的String与在接受的答案中对Uri对象调用toString()相同。

I know this is an older post but I hope this can help others who still find their way here.我知道这是一篇较旧的帖子,但我希望这可以帮助仍然在这里找到方法的其他人。

Android 10 introduces scoped storage, so the you will get a file not found EACCESS error trying to write on those folders. Android 10 引入了作用域存储,因此您将在尝试写入这些文件夹时遇到 EACCESS 未找到文件错误。 Here a solution in kotlin, following the current android documentation.这是 kotlin 中的解决方案,遵循当前的 android 文档。

fun addImageToGallery(b: Bitmap): Uri {
    val resolver = context.applicationContext.contentResolver

    val pictureCollection = MediaStore.Images.Media
        .getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)

    val pictureDetails = ContentValues().apply {
        put(MediaStore.Images.Media.DISPLAY_NAME, "CurrentAlbumArt.png")
        put(MediaStore.Audio.Media.IS_PENDING, 1)
    }

    val pictureContentUri = resolver.insert(pictureCollection, pictureDetails)!!

    resolver.openFileDescriptor(pictureContentUri, "w", null).use { pfd ->
        try {
            pfd?.let {
                val fos = FileOutputStream(it.fileDescriptor)
                b.compress(Bitmap.CompressFormat.PNG, 100, fos)
                fos.close()
            }
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    pictureDetails.clear()
    pictureDetails.put(MediaStore.Images.Media.IS_PENDING, 0)
    resolver.update(pictureContentUri, pictureDetails, null, null)
    return pictureContentUri
}

Note: I overwrite the old files (same file name) and insert works as replace as i needed for my case.注意:我覆盖了旧文件(相同的文件名)并根据我的情况插入作为替换。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM