android - salvar imagem na galeria

90

tenho um aplicativo com galeria de imagens e quero que o usuário possa salvá-lo em sua própria galeria. Eu criei um menu de opções com uma única voz "salvar" para permitir isso, mas o problema é ... como posso salvar a imagem na galeria?

este é o meu código:

@Override
        public boolean onOptionsItemSelected(MenuItem item) {
            // Handle item selection
            switch (item.getItemId()) {
            case R.id.menuFinale:

                imgView.setDrawingCacheEnabled(true);
                Bitmap bitmap = imgView.getDrawingCache();
                File root = Environment.getExternalStorageDirectory();
                File file = new File(root.getAbsolutePath()+"/DCIM/Camera/img.jpg");
                try 
                {
                    file.createNewFile();
                    FileOutputStream ostream = new FileOutputStream(file);
                    bitmap.compress(CompressFormat.JPEG, 100, ostream);
                    ostream.close();
                } 
                catch (Exception e) 
                {
                    e.printStackTrace();
                }



                return true;
            default:
                return super.onOptionsItemSelected(item);
            }
        }

não tenho certeza desta parte do código:

File root = Environment.getExternalStorageDirectory();
                File file = new File(root.getAbsolutePath()+"/DCIM/Camera/img.jpg");

é correto salvar na galeria? infelizmente, o código não funciona :(

Christian Giupponi
fonte
você resolveu esse problema? você pode compartilhar comigo
user3233280
Eu também estou tendo o mesmo problema stackoverflow.com/questions/21951558/…
user3233280
Para aqueles que ainda estão tendo problemas para salvar o arquivo, pode ser porque o seu url contém caracteres ilegais como "?", ":" E "-" Remova esses e deve funcionar. Este é um erro comum em dispositivos externos e emuladores de Android. Veja mais sobre isso aqui: stackoverflow.com/questions/11394616/…
ChallengeAccepted em
A resposta aceita está um pouco desatualizada em 2019. Escrevi uma resposta atualizada aqui: stackoverflow.com/questions/36624756/…
Bao Lei

Respostas:

168
MediaStore.Images.Media.insertImage(getContentResolver(), yourBitmap, yourTitle , yourDescription);

O código anterior adicionará a imagem no final da galeria. Se você quiser modificar a data para que apareça no início ou qualquer outro metadado, consulte o código abaixo (Cortesia de SK, samkirton ):

https://gist.github.com/samkirton/0242ba81d7ca00b475b9

/**
 * Android internals have been modified to store images in the media folder with 
 * the correct date meta data
 * @author samuelkirton
 */
public class CapturePhotoUtils {

    /**
     * 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(ContentResolver, Bitmap, String, String)
     */
    public static final String insertImage(ContentResolver cr, 
            Bitmap source, 
            String title, 
            String description) {

        ContentValues values = new ContentValues();
        values.put(Images.Media.TITLE, title);
        values.put(Images.Media.DISPLAY_NAME, title);
        values.put(Images.Media.DESCRIPTION, description);
        values.put(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(Images.Media.DATE_ADDED, System.currentTimeMillis());
        values.put(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 = Images.Thumbnails.getThumbnail(cr, id, Images.Thumbnails.MINI_KIND, null);
                // This is for backward compatibility.
                storeThumbnail(cr, miniThumb, id, 50F, 50F,Images.Thumbnails.MICRO_KIND);
            } else {
                cr.delete(url, null, null);
                url = null;
            }
        } catch (Exception e) {
            if (url != null) {
                cr.delete(url, null, null);
                url = null;
            }
        }

        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 static final 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(Images.Thumbnails.KIND,kind);
        values.put(Images.Thumbnails.IMAGE_ID,(int)id);
        values.put(Images.Thumbnails.HEIGHT,thumb.getHeight());
        values.put(Images.Thumbnails.WIDTH,thumb.getWidth());

        Uri url = cr.insert(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) {
            return null;
        } catch (IOException ex) {
            return null;
        }
    }
}
Fratini
fonte
22
Isso salva a imagem, mas no final da galeria, quando você tira uma foto com a câmera, ela é salva no topo. Como posso salvar a imagem no topo da galeria?
eric.itzhak
19
Observe que você também deve adicionar <uses-permission android: name = "android.permission.WRITE_EXTERNAL_STORAGE" /> ao seu manifext.xml.
Kyle Clegg
3
As imagens não são salvas na parte superior da galeria porque, internamente, insertImage não adiciona nenhum metadado de data. Consulte este GIST: gist.github.com/0242ba81d7ca00b475b9.git é uma cópia exata do método insertImage, mas adiciona a data meta data para garantir que a imagem seja adicionada na frente da galeria.
S-K
1
@ S-K'Não consigo acessar esse URL. Atualize-o e atualizarei minha resposta para que tenha as duas opções. Saúde
sfratini
6
Aqui está o link GIST correto mencionado acima (necessário para remover o .gitno final)
minipif
47

Na verdade, você pode salvar sua foto em qualquer lugar. Se você deseja salvar em um espaço público, para que qualquer outro aplicativo possa acessar, use este código:

storageDir = new File(
    Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_PICTURES
    ), 
    getAlbumName()
);

A foto não vai para o álbum. Para fazer isso, você precisa chamar uma varredura:

private void galleryAddPic() {
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    File f = new File(mCurrentPhotoPath);
    Uri contentUri = Uri.fromFile(f);
    mediaScanIntent.setData(contentUri);
    this.sendBroadcast(mediaScanIntent);
}

Você pode encontrar mais informações em https://developer.android.com/training/camera/photobasics.html#TaskGallery

Sigrist
fonte
1
Esta é uma solução simples e agradável, pois não precisamos alterar toda a implementação e podemos criar uma pasta personalizada para os aplicativos.
Hugo Gresse,
2
Enviar uma transmissão pode ser um desperdício de recursos quando você pode digitalizar apenas um arquivo: stackoverflow.com/a/5814533/43051 .
Jérémy Reynaud
Para onde você realmente passa o bitmap?
Daniel Reyhanian
20

Eu tentei muitas coisas para deixar isso funcionar em Marshmallow e Lollipop. Finalmente, acabei movendo a imagem salva para a pasta DCIM (o novo aplicativo Google Photo digitaliza imagens apenas se elas estiverem aparentemente dentro desta pasta)

public static File createImageFile() throws IOException {
    // Create an image file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
         .format(System.currentTimeInMillis());
    File storageDir = new File(Environment
         .getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/Camera/");
    if (!storageDir.exists())
        storageDir.mkdirs();
    File image = File.createTempFile(
            timeStamp,                   /* prefix */
            ".jpeg",                     /* suffix */
            storageDir                   /* directory */
    );
    return image;
}

E então o código padrão para escanear arquivos que você também pode encontrar no site Google Developers .

public static void addPicToGallery(Context context, String photoPath) {
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    File f = new File(photoPath);
    Uri contentUri = Uri.fromFile(f);
    mediaScanIntent.setData(contentUri);
    context.sendBroadcast(mediaScanIntent);
}

Lembre-se de que esta pasta não pode estar presente em todos os dispositivos do mundo e que a partir do Marshmallow (API 23), você precisa solicitar a permissão WRITE_EXTERNAL_STORAGE para o usuário.

MatPag
fonte
1
Obrigado pelas informações sobre o Google Fotos.
Jérémy Reynaud
1
Esta é a única solução que explica bem. Ninguém mais mencionou que o arquivo deve estar na pasta DCIM. Obrigado!!!
Predrag Manojlovic
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)fez o truque para mim. Obrigado!
saltandpepper de
2
getExternalStoragePublicDirectory()agora está obsoleto na API 29. Need to use MediaStore
riggaroo
@riggaroo Sim, você está certa Rebecca, irei atualizar a resposta o mais rápido possível
MatPag
12

De acordo com este curso , a maneira correta de fazer isso é:

Environment.getExternalStoragePublicDirectory(
        Environment.DIRECTORY_PICTURES
    )

Isso fornecerá o caminho raiz para o diretório da galeria.

Cédric Julien
fonte
Tentei este novo código, mas travou java.lang.NoSuchFieldError: android.os.Environment.DIRECTORY_PICTURES
Christian Giupponi
ok obrigado, então não há como colocar uma imagem na galeria com android <2.2?
Christian Giupponi
Perfeito - um link direto para o site do desenvolvedor Android. Isso funcionou e foi uma solução simples.
Phil
1
Boa resposta, mas seria melhor com a adição do método "galleryAddPic" das outras respostas aqui, já que normalmente você vai querer que o aplicativo Gallery note novas fotos.
Andrew Koster
11
private void galleryAddPic() {
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    File f = new File(mCurrentPhotoPath);
    Uri contentUri = Uri.fromFile(f);
    mediaScanIntent.setData(contentUri);
    this.sendBroadcast(mediaScanIntent);
}
nitin Sol
fonte
6

Você pode criar um diretório dentro da pasta da câmera e salvar a imagem. Depois disso, você pode simplesmente realizar sua varredura. Ele mostrará instantaneamente sua imagem na galeria.

String root = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).toString()+ "/Camera/Your_Directory_Name";
File myDir = new File(root);
myDir.mkdirs();
String fname = "Image-" + image_name + ".png";
File file = new File(myDir, fname);
System.out.println(file.getAbsolutePath());
if (file.exists()) file.delete();
    Log.i("LOAD", root + fname);
    try {
        FileOutputStream out = new FileOutputStream(file);
        finalBitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
        out.flush();
        out.close();
    } catch (Exception e) {
       e.printStackTrace();
    }

MediaScannerConnection.scanFile(context, new String[]{file.getPath()}, new String[]{"image/jpeg"}, null);
Javatar
fonte
neste critério esta é a melhor resposta
Noor Hossain
1

Venho aqui com a mesma dúvida, mas para Xamarin para Android, usei a resposta de Sigrist para fazer este método após salvar meu arquivo:

private void UpdateGallery()
{
    Intent mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
    Java.IO.File file = new Java.IO.File(_path);
    Android.Net.Uri contentUri = Android.Net.Uri.FromFile(file);
    mediaScanIntent.SetData(contentUri);
    Application.Context.SendBroadcast(mediaScanIntent);
} 

e resolveu meu problema, Thx Sigrist. Coloquei aqui porque não encontrei um answare sobre isso para o Xamarin e espero que possa ajudar outras pessoas.

Slaters
fonte
1

No meu caso, as soluções acima não funcionaram, tive que fazer o seguinte:

sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(f)));
dc10
fonte
é muito bom saber sobre esta opção, mas infelizmente não funciona em alguns dispositivos com Android 6, então é ContentProviderpreferível solytion
Siarhei
0
 String filePath="/storage/emulated/0/DCIM"+app_name;
    File dir=new File(filePath);
    if(!dir.exists()){
        dir.mkdir();
    }

Este código está no método onCreate. Este código é para criar um diretório de app_name. Agora, este diretório pode ser acessado usando o aplicativo gerenciador de arquivos padrão no android. Use esta string filePath sempre que necessário para definir sua pasta de destino. Tenho certeza de que esse método funciona no Android 7 também porque testei nele. Portanto, ele pode funcionar em outras versões do Android também.

Bhavik Mehta
fonte