Salvar bitmap no local


469

Estou trabalhando em uma função para baixar uma imagem de um servidor da Web, exibi-la na tela e, se o usuário desejar manter a imagem, salve-a no cartão SD em uma determinada pasta. Existe uma maneira fácil de pegar um bitmap e salvá-lo no cartão SD em uma pasta de minha escolha?

Meu problema é que posso baixar a imagem e exibi-la na tela como um bitmap. A única maneira que consegui encontrar para salvar uma imagem em uma pasta específica é usar o FileOutputStream, mas isso requer uma matriz de bytes. Não sei como converter (se esse for o caminho certo) de Bitmap para matriz de bytes, para que eu possa usar um FileOutputStream para gravar os dados.

A outra opção que tenho é usar o MediaStore:

MediaStore.Images.Media.insertImage(getContentResolver(), bm,
    barcodeNumber + ".jpg Card Image", barcodeNumber + ".jpg Card Image");

O que funciona bem para salvar no cartão SD, mas não permite que você personalize a pasta.


Exatamente o que estou fazendo no meu aplicativo. Faço o download de um servidor Web de formulário de imagem grande, manipulo-o e carrego o bitmap diretamente em uma visualização de imagem mImage.setImageBitmap(_result.getBitmap());no meu onTaskComplete()retorno de chamada. Agora, tenho que permitir que os usuários salvem o arquivo localmente, se quiserem, através do menu de contexto de pressão longa. Eu deveria poder usar a solução abaixo. O que eu quero saber, porém, você descobriu uma abordagem melhor para isso?
wired00

Há uma maneira maneira elegante de fazê-lo aqui: stackoverflow.com/questions/4263375/...
Chepech

Respostas:


921
try (FileOutputStream out = new FileOutputStream(filename)) {
    bmp.compress(Bitmap.CompressFormat.PNG, 100, out); // bmp is your Bitmap instance
    // PNG is a lossless format, the compression factor (100) is ignored
} catch (IOException e) {
    e.printStackTrace();
}

11
Também comprimi a imagem, mas para 100%, e quando estou obtendo minha imagem na tela, ela é muito pequena. qualquer razão?
AZ_

3
@ Aizaz Isso não altera o tamanho da imagem, apenas o formato e a qualidade (potencialmente). Também é importante notar que a qualidade da compactação 90, no exemplo acima, não terá nenhum efeito ao salvar como PNG, mas fará a diferença para os JPEGs. No caso de um JPEG, você pode escolher qualquer número entre 0 e 100.
lavrador

1
Deve-se observar que salvar dessa maneira para .JPEG com 100% de qualidade realmente salvará uma imagem diferente da original na Web (pelo menos ocupará muito mais espaço). Considere uma abordagem alternativa.
Warpzit

42
É preciso recomprimir? Eu só quero salvar a imagem original.
Hein du Plessis

2
@HeinduPlessis Não precisa, mas você provavelmente deveria. Salvar o bitmap bruto requer muito mais espaço, dependendo do formato (ARGB_4444 vs ARGB_8888, por exemplo).
Irwinb 13/04

134

Você deve usar o Bitmap.compress()método para salvar um bitmap como um arquivo. Ele comprimirá (se o formato usado permitir) sua imagem e enviará para um OutputStream.

Aqui está um exemplo de uma instância de Bitmap obtida através getImageBitmap(myurl)que pode ser compactada como um JPEG com uma taxa de compactação de 85%:

// Assume block needs to be inside a Try/Catch block.
String path = Environment.getExternalStorageDirectory().toString();
OutputStream fOut = null;
Integer counter = 0;
File file = new File(path, "FitnessGirl"+counter+".jpg"); // the File to save , append increasing numeric counter to prevent files from getting overwritten.
fOut = new FileOutputStream(file);

Bitmap pictureBitmap = getImageBitmap(myurl); // obtaining the Bitmap
pictureBitmap.compress(Bitmap.CompressFormat.JPEG, 85, fOut); // saving the Bitmap to a file compressed as a JPEG with 85% compression rate
fOut.flush(); // Not really required
fOut.close(); // do not forget to close the stream

MediaStore.Images.Media.insertImage(getContentResolver(),file.getAbsolutePath(),file.getName(),file.getName());

@JoaquinG há alguma razão para isso, fOut.flush()não é possível omitir?
Niklas

@ Niklas Eu acho que você pode omitir flush.
precisa saber é o seguinte

1
Você deve alterar a redação de "taxa de compressão de 85%" para "taxa de qualidade de 85%" para menos ambiguidade. Eu interpretaria "taxa de compressão de 85%" como "15% de qualidade", mas o parâmetro int Bitmap.compressespecifica a qualidade.
Tim Cooke

você pode plz postar o métodogetImageBitmap(myurl)
Aashish

38
outStream = new FileOutputStream(file);

lançará uma exceção sem permissão no AndroidManifest.xml (pelo menos no os2.2):

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

10
Não se o seu arquivo absolutePath for um caminho interno?
Blundell

24

Dentro onActivityResult:

String filename = "pippo.png";
File sd = Environment.getExternalStorageDirectory();
File dest = new File(sd, filename);

Bitmap bitmap = (Bitmap)data.getExtras().get("data");
try {
     FileOutputStream out = new FileOutputStream(dest);
     bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
     out.flush();
     out.close();
} catch (Exception e) {
     e.printStackTrace();
}

7
você está chamando-o de "pippo.jpg" mas você está usando compressão PNG
Ralphleon

1
O formato de compactação deve ser .JPEG se você quiser alterar a qualidade do bitmap. A qualidade não pode ser alterada no formato PNG.
Mustafa Güven

13

Alguns formatos, como PNG, sem perdas, ignoram a configuração de qualidade.


2
PNG ainda é um formato compactado. A configuração de qualidade não modifica a qualidade da compactação?
Tamás Barta

O estado dos documentos (destacando por mim): Dica para o compressor, 0-100. 0 significa comprimir para tamanho pequeno, 100 significa comprimir para qualidade máxima. Alguns formatos, como PNG, que é sem perdas, irá ignorar a configuração de qualidade
Stephan Henningsen

10

Aqui está o código de exemplo para salvar o bitmap no arquivo:

public static File savebitmap(Bitmap bmp) throws IOException {
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    bmp.compress(Bitmap.CompressFormat.JPEG, 60, bytes);
    File f = new File(Environment.getExternalStorageDirectory()
            + File.separator + "testimage.jpg");
    f.createNewFile();
    FileOutputStream fo = new FileOutputStream(f);
    fo.write(bytes.toByteArray());
    fo.close();
    return f;
}

Agora chame esta função para salvar o bitmap na memória interna.

File newfile = savebitmap(bitmap);

Espero que ajude você. Feliz codificação da vida.


8
Bitmap bbicon;

bbicon=BitmapFactory.decodeResource(getResources(),R.drawable.bannerd10);
//ByteArrayOutputStream baosicon = new ByteArrayOutputStream();
//bbicon.compress(Bitmap.CompressFormat.PNG,0, baosicon);
//bicon=baosicon.toByteArray();

String extStorageDirectory = Environment.getExternalStorageDirectory().toString();
OutputStream outStream = null;
File file = new File(extStorageDirectory, "er.PNG");
try {
    outStream = new FileOutputStream(file);
    bbicon.compress(Bitmap.CompressFormat.PNG, 100, outStream);
    outStream.flush();
    outStream.close();
} catch(Exception e) {

}

1
você não precisa liberar o outStream se estiver passando isso no método 'compress'. esse método fará o seu nome.
Dharam

7

Por que não chamar o Bitmap.compressmétodo com 100 (que parece sem perdas)?


Mesmo que seja ignorado, deve ser 100. Se algum dia o formato de compactação for alterado para um pouco flexível, a imagem corresponderá mais de perto a ela. Observe também que se você tiver um código que abstraia essa chamada, isso talvez seja mais importante.
precisa saber é o seguinte

2
100% não tem perdas com JPEG, FWIW. Você pode verificar isso carregando e salvando o bitmap repetidamente.

5

Eu também gostaria de salvar uma foto. Mas o meu problema (?) É que eu quero salvá-lo de um bitmap que eu desenhei.

Eu fiz assim:

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
            switch (item.getItemId()) {
            case R.id.save_sign:      

                myView.save();
                break;

            }
            return false;    

    }

public void save() {
            String filename;
            Date date = new Date(0);
            SimpleDateFormat sdf = new SimpleDateFormat ("yyyyMMddHHmmss");
            filename =  sdf.format(date);

            try{
                 String path = Environment.getExternalStorageDirectory().toString();
                 OutputStream fOut = null;
                 File file = new File(path, "/DCIM/Signatures/"+filename+".jpg");
                 fOut = new FileOutputStream(file);

                 mBitmap.compress(Bitmap.CompressFormat.JPEG, 85, fOut);
                 fOut.flush();
                 fOut.close();

                 MediaStore.Images.Media.insertImage(getContentResolver()
                 ,file.getAbsolutePath(),file.getName(),file.getName());

            }catch (Exception e) {
                e.printStackTrace();
            }

 }

seu método save só funciona para mim .. depois de perder algumas horas .. muito obrigado senhor.
Mohammed Sufian 04/04

5

A maneira que eu encontrei para enviar PNG e transparência.

String file_path = Environment.getExternalStorageDirectory().getAbsolutePath() +
                    "/CustomDir";
File dir = new File(file_path);
if(!dir.exists())
  dir.mkdirs();

String format = new SimpleDateFormat("yyyyMMddHHmmss",
       java.util.Locale.getDefault()).format(new Date());

File file = new File(dir, format + ".png");
FileOutputStream fOut;
try {
        fOut = new FileOutputStream(file);
        yourbitmap.compress(Bitmap.CompressFormat.PNG, 85, fOut);
        fOut.flush();
        fOut.close();
     } catch (Exception e) {
        e.printStackTrace();
 }

Uri uri = Uri.fromFile(file);     
Intent intent = new Intent(android.content.Intent.ACTION_SEND);
intent.setType("image/*");
intent.putExtra(android.content.Intent.EXTRA_SUBJECT, "");
intent.putExtra(android.content.Intent.EXTRA_TEXT, "");
intent.putExtra(Intent.EXTRA_STREAM, uri);

startActivity(Intent.createChooser(intent,"Sharing something")));

O valor 85 aqui não faz sentido, pois o PNG não tem perdas. A documentação diz - `Alguns formatos, como PNG, sem perdas, ignoram a configuração de qualidade` #
Minhaz 11/10/19

2

Crie uma miniatura de vídeo para um vídeo. Pode retornar nulo se o vídeo estiver corrompido ou o formato não for suportado.

private void makeVideoPreview() {
    Bitmap thumbnail = ThumbnailUtils.createVideoThumbnail(videoAbsolutePath, MediaStore.Images.Thumbnails.MINI_KIND);
    saveImage(thumbnail);
}

Para salvar seu bitmap em sdcard, use o seguinte código

Imagem da loja

private void storeImage(Bitmap image) {
    File pictureFile = getOutputMediaFile();
    if (pictureFile == null) {
        Log.d(TAG,
                "Error creating media file, check storage permissions: ");// e.getMessage());
        return;
    } 
    try {
        FileOutputStream fos = new FileOutputStream(pictureFile);
        image.compress(Bitmap.CompressFormat.PNG, 90, fos);
        fos.close();
    } catch (FileNotFoundException e) {
        Log.d(TAG, "File not found: " + e.getMessage());
    } catch (IOException e) {
        Log.d(TAG, "Error accessing file: " + e.getMessage());
    }  
}

Para obter o caminho para o armazenamento de imagens

/** Create a File for saving an image or video */
private  File getOutputMediaFile(){
    // To be safe, you should check that the SDCard is mounted
    // using Environment.getExternalStorageState() before doing this. 
    File mediaStorageDir = new File(Environment.getExternalStorageDirectory()
            + "/Android/data/"
            + getApplicationContext().getPackageName()
            + "/Files"); 

    // This location works best if you want the created images to be shared
    // between applications and persist after your app has been uninstalled.

    // Create the storage directory if it does not exist
    if (! mediaStorageDir.exists()){
        if (! mediaStorageDir.mkdirs()){
            return null;
        }
    } 
    // Create a media file name
    String timeStamp = new SimpleDateFormat("ddMMyyyy_HHmm").format(new Date());
    File mediaFile;
        String mImageName="MI_"+ timeStamp +".jpg";
        mediaFile = new File(mediaStorageDir.getPath() + File.separator + mImageName);  
    return mediaFile;
} 

2

Você deseja salvar o Bitmap no diretório de sua escolha. Criei uma biblioteca ImageWorker que permite ao usuário carregar, salvar e converter imagens bitmaps / drawables / base64.

SDK mínimo - 14

Pré-requisito

  • Salvar arquivos exigiria permissão WRITE_EXTERNAL_STORAGE.
  • A recuperação de arquivos exigiria a permissão READ_EXTERNAL_STORAGE.

Salvando Bitmap / Drawable / Base64

ImageWorker.to(context).
    directory("ImageWorker").
    subDirectory("SubDirectory").
    setFileName("Image").
    withExtension(Extension.PNG).
    save(sourceBitmap,85)

Carregando bitmap

val bitmap: Bitmap? = ImageWorker.from(context).
    directory("ImageWorker").
    subDirectory("SubDirectory").
    setFileName("Image").
    withExtension(Extension.PNG).
    load()

Implementação

Adicionar dependências

No nível do projeto Gradle

allprojects {
        repositories {
            ...
            maven { url 'https://jitpack.io' }
        }
    }

No nível do aplicativo Gradle

dependencies {
            implementation 'com.github.ihimanshurawat:ImageWorker:0.51'
    }

Você pode ler mais em https://github.com/ihimanshurawat/ImageWorker/blob/master/README.md


1

Ei, apenas dê o nome para .bmp

Faça isso:

ByteArrayOutputStream bytes = new ByteArrayOutputStream();
_bitmapScaled.compress(Bitmap.CompressFormat.PNG, 40, bytes);

//you can create a new file name "test.BMP" in sdcard folder.
File f = new File(Environment.getExternalStorageDirectory()
                        + File.separator + "**test.bmp**")

vai parecer que estou enganando, mas tente uma vez que será salvo no forame bmp ..


1

Verifique se o diretório foi criado antes de ligar bitmap.compress:

new File(FileName.substring(0,FileName.lastIndexOf("/"))).mkdirs();

1

Após o Android 4.4 Kitkat, e a partir de 2017, o compartilhamento do Android 4.4 e menos é de cerca de 20% e diminui, não é possível salvar no cartão SD usando Fileclasse e getExternalStorageDirectory()método. Esse método retorna a memória interna do dispositivo e as imagens são salvas visíveis para todos os aplicativos. Você também pode salvar imagens privadas apenas em seu aplicativo e ser excluído quando o usuário excluir seu aplicativo comopenFileOutput() método

A partir do Android 6.0, você pode formatar seu cartão SD como uma memória interna, mas apenas privada para o seu dispositivo. (Se você formatar um carro SD como memória interna, apenas seu dispositivo poderá acessar ou ver seu conteúdo). outras respostas, mas se você quiser usar um cartão SD removível, leia minha resposta abaixo.

Você deve usar o Storage Access Framework para obter o onActivityResultmétodo de atividade da interface do usuário para a pasta para obter a pasta selecionada pelo usuário e adicionar permissão persistente de recuperação para poder acessar a pasta depois que o usuário reiniciar o dispositivo.

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

    if (resultCode == RESULT_OK) {

        // selectDirectory() invoked
        if (requestCode == REQUEST_FOLDER_ACCESS) {

            if (data.getData() != null) {
                Uri treeUri = data.getData();
                tvSAF.setText("Dir: " + data.getData().toString());
                currentFolder = treeUri.toString();
                saveCurrentFolderToPrefs();

                // grantUriPermission(getPackageName(), treeUri,
                // Intent.FLAG_GRANT_READ_URI_PERMISSION |
                // Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

                final int takeFlags = data.getFlags()
                        & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                // Check for the freshest data.
                getContentResolver().takePersistableUriPermission(treeUri, takeFlags);

            }
        }
    }
}

Agora, salve a pasta Salvar nas preferências compartilhadas para não solicitar ao usuário que selecione a pasta toda vez que quiser salvar uma imagem.

Você deve usar a DocumentFileclasse para salvar sua imagem, não Fileou ParcelFileDescriptor, para obter mais informações, consulte este tópico para salvar a imagem no cartão SD com compress(CompressFormat.JPEG, 100, out);método e DocumentFileclasses.


1

Alguns novos dispositivos não salvam bitmap. Então, eu expliquei um pouco mais ..

verifique se você adicionou abaixo de Permission

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

e crie um arquivo xml sob o xmlnome da pasta provider_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path
        name="external_files"
        path="." />
</paths>

e no AndroidManifest em

<provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>

simplesmente chame saveBitmapFile (passYourBitmapHere)

public static void saveBitmapFile(Bitmap bitmap) throws IOException {
        File mediaFile = getOutputMediaFile();
        FileOutputStream fileOutputStream = new FileOutputStream(mediaFile);
        bitmap.compress(Bitmap.CompressFormat.JPEG, getQualityNumber(bitmap), fileOutputStream);
        fileOutputStream.flush();
        fileOutputStream.close();
    }

Onde

File getOutputMediaFile() {
        File mediaStorageDir = new File(
                Environment.getExternalStorageDirectory(),
                "easyTouchPro");
        if (mediaStorageDir.isDirectory()) {

            // Create a media file name
            String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
                    .format(Calendar.getInstance().getTime());
            String mCurrentPath = mediaStorageDir.getPath() + File.separator
                            + "IMG_" + timeStamp + ".jpg";
            File mediaFile = new File(mCurrentPath);
            return mediaFile;
        } else { /// error handling for PIE devices..
            mediaStorageDir.delete();
            mediaStorageDir.mkdirs();
            galleryAddPic(mediaStorageDir);

            return (getOutputMediaFile());
        }
    }

e outros métodos

public static int getQualityNumber(Bitmap bitmap) {
        int size = bitmap.getByteCount();
        int percentage = 0;

        if (size > 500000 && size <= 800000) {
            percentage = 15;
        } else if (size > 800000 && size <= 1000000) {
            percentage = 20;
        } else if (size > 1000000 && size <= 1500000) {
            percentage = 25;
        } else if (size > 1500000 && size <= 2500000) {
            percentage = 27;
        } else if (size > 2500000 && size <= 3500000) {
            percentage = 30;
        } else if (size > 3500000 && size <= 4000000) {
            percentage = 40;
        } else if (size > 4000000 && size <= 5000000) {
            percentage = 50;
        } else if (size > 5000000) {
            percentage = 75;
        }

        return percentage;
    }

e

void galleryAddPic(File f) {
        Intent mediaScanIntent = new Intent(
                "android.intent.action.MEDIA_SCANNER_SCAN_FILE");
        Uri contentUri = Uri.fromFile(f);
        mediaScanIntent.setData(contentUri);

        this.sendBroadcast(mediaScanIntent);
    }

Você tem mais informações sobre error handling for PIE devices..e acho que a recursão getOutputMediaFilepode ser um loop infinito se a solução alternativa falhar.
Raimund Wege

0

Salve o bitmap na sua galeria sem compactar.

private File saveBitMap(Context context, Bitmap Final_bitmap) {
    File pictureFileDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Your Folder Name");
    if (!pictureFileDir.exists()) {
        boolean isDirectoryCreated = pictureFileDir.mkdirs();
        if (!isDirectoryCreated)
            Log.i("TAG", "Can't create directory to save the image");
        return null;
    }
    String filename = pictureFileDir.getPath() + File.separator + System.currentTimeMillis() + ".jpg";
    File pictureFile = new File(filename);
    try {
        pictureFile.createNewFile();
        FileOutputStream oStream = new FileOutputStream(pictureFile);
        Final_bitmap.compress(Bitmap.CompressFormat.PNG, 100, oStream);
        oStream.flush();
        oStream.close();
        Toast.makeText(Full_Screen_Activity.this, "Save Image Successfully..", Toast.LENGTH_SHORT).show();
    } catch (IOException e) {
        e.printStackTrace();
        Log.i("TAG", "There was an issue saving the image.");
    }
    scanGallery(context, pictureFile.getAbsolutePath());
    return pictureFile;
}
private void scanGallery(Context cntx, String path) {
    try {
        MediaScannerConnection.scanFile(cntx, new String[]{path}, null, new MediaScannerConnection.OnScanCompletedListener() {
            public void onScanCompleted(String path, Uri uri) {
                Toast.makeText(Full_Screen_Activity.this, "Save Image Successfully..", Toast.LENGTH_SHORT).show();
            }
        });
    } catch (Exception e) {
        e.printStackTrace();
        Log.i("TAG", "There was an issue scanning gallery.");
    }
}

-1

// | == | Crie um arquivo PNG a partir do bitmap:

void devImjFylFnc(String pthAndFylTtlVar, Bitmap iptBmjVar)
{
    try
    {
        FileOutputStream fylBytWrtrVar = new FileOutputStream(pthAndFylTtlVar);
        iptBmjVar.compress(Bitmap.CompressFormat.PNG, 100, fylBytWrtrVar);
        fylBytWrtrVar.close();
    }
    catch (Exception errVar) { errVar.printStackTrace(); }
}

// | == | Obtenha o Bimap do arquivo:

Bitmap getBmjFrmFylFnc(String pthAndFylTtlVar)
{
    return BitmapFactory.decodeFile(pthAndFylTtlVar);
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.