Guardar mapa de bits en la ubicación

469

Estoy trabajando en una función para descargar una imagen de un servidor web, mostrarla en la pantalla y, si el usuario desea conservar la imagen, guardarla en la tarjeta SD en una carpeta determinada. ¿Hay una manera fácil de tomar un mapa de bits y simplemente guardarlo en la tarjeta SD en una carpeta de mi elección?

Mi problema es que puedo descargar la imagen, mostrarla en la pantalla como un mapa de bits. La única forma en que pude encontrar para guardar una imagen en una carpeta en particular es usar FileOutputStream, pero eso requiere una matriz de bytes. No estoy seguro de cómo convertir (si esta es la forma correcta) de Bitmap a matriz de bytes, por lo que puedo usar un FileOutputStream para escribir los datos.

La otra opción que tengo es usar MediaStore:

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

Lo cual funciona bien para guardar en la tarjeta SD, pero no le permite personalizar la carpeta.

Chrispix
fuente
Exactamente lo que estoy haciendo en mi aplicación. Descargo una imagen grande del servidor web, la manipulo y cargo el mapa de bits directamente en una vista de imagen a través mImage.setImageBitmap(_result.getBitmap());de mi onTaskComplete()devolución de llamada. Ahora tengo que permitir que los usuarios guarden el archivo localmente si lo desean a través del menú contextual de pulsación larga. Debería poder usar la solución a continuación. Sin embargo, lo que quiero saber, ¿descubriste un mejor enfoque para esto?
wired00
Hay una manera elegante de hacerlo aquí: stackoverflow.com/questions/4263375/…
Chepech

Respuestas:

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();
}
Ulrich Scheller
fuente
11
También comprimí la imagen pero al 100 por ciento y cuando obtengo mi imagen en lienzo es muy pequeña. ¿cualquier razón?
AZ_
3
@Aizaz Esto no cambiará el tamaño de la imagen, solo el formato y (potencialmente) la calidad. También vale la pena señalar que la calidad de compresión 90, en el ejemplo anterior, no tendrá ningún efecto al guardar como PNG, pero hará una diferencia para los JPEG. En el caso de un JPEG, puede elegir cualquier número entre 0 y 100.
labrador
1
Cabe señalar que guardar de esta manera para .JPEG con una calidad del 100% en realidad guardará una imagen diferente a la original en la web (al menos ocupará mucho más espacio). Considere un enfoque alternativo.
Warpzit
42
¿Se tienen recomprimir? Solo quiero guardar la imagen original.
Hein du Plessis
2
@HeinduPlessis No tiene que hacerlo, pero probablemente debería hacerlo. Guardar el mapa de bits sin formato ocupará mucho más espacio, según el formato (ARGB_4444 frente a ARGB_8888, por ejemplo).
irwinb
134

Debe usar el Bitmap.compress()método para guardar un mapa de bits como un archivo. Comprimirá (si el formato utilizado lo permite) su imagen y la insertará en un OutputStream.

Aquí hay un ejemplo de una instancia de mapa de bits obtenida a través de getImageBitmap(myurl)que se puede comprimir como JPEG con una tasa de compresión del 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
fuente
@JoaquinG alguna razón para eso, fOut.flush()¿no es posible omitirlo?
Niklas
@ Niklas Creo que puedes omitir el rubor.
JoaquinG
1
Debe cambiar la redacción de "tasa de compresión del 85%" a "tasa de calidad del 85%" para reducir la ambigüedad. Interpretaría que "tasa de compresión del 85%" significa "15% de calidad", pero el parámetro int de Bitmap.compressespecifica calidad.
Tim Cooke
¿puedes publicar el método?getImageBitmap(myurl)
Aashish
38
outStream = new FileOutputStream(file);

lanzará una excepción sin permiso en AndroidManifest.xml (al menos en os2.2):

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
user996042
fuente
10
¿No si su archivo absolutePath es una ruta interna?
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();
}
Alessandro
fuente
77
lo estás llamando "pippo.jpg" pero estás usando compresión PNG
Ralphleon
1
el formato de compresión debe ser .JPEG si desea cambiar la calidad del mapa de bits. La calidad no se puede cambiar en formato PNG.
Mustafa Güven
13

Algunos formatos, como PNG sin pérdida, ignorarán la configuración de calidad.

shinydev
fuente
2
PNG sigue siendo un formato comprimido. ¿La configuración de calidad no modifica la calidad de compresión?
Tamás Barta el
El estado de los documentos (resaltado por mí): Sugerencia para el compresor, 0-100. 0 significa compresa para tamaño pequeño, 100 significa compresa para calidad máxima. Algunos formatos, como PNG sin pérdida, ignorarán la configuración de calidad
Stephan Henningsen
10

Aquí está el código de muestra para guardar el mapa de bits en el archivo:

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;
}

Ahora llame a esta función para guardar el mapa de bits en la memoria interna.

File newfile = savebitmap(bitmap);

Espero que te ayude. Feliz vida de codificación.

A-Droid Tech
fuente
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) {

}
Ashish Anand
fuente
1
no necesita vaciar el OutStream si está pasando eso en el método 'comprimir'. ese método lo hará por usted.
dharam
7

¿Por qué no llamar al Bitmap.compressmétodo con 100 (que parece que no tiene pérdidas)?

TofuBeer
fuente
Aunque se ignore, debería ser 100. Si algún día el formato de compresión se cambia a uno suelto, la imagen coincidirá más estrechamente. También tenga en cuenta que si tiene un código que abstraiga esta llamada, esto quizás sea más importante.
ddcruver
2
100% no es sin pérdida con JPEG, FWIW. Puede verificar esto cargando y guardando el mapa de bits repetidamente.
5

También me gustaría guardar una foto. Pero mi problema (?) Es que quiero guardarlo desde un mapa de bits que he dibujado.

Lo hice así:

 @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();
            }

 }
usuario511895
fuente
su método de salvar solo funciona para mí ... después de perder algunas horas ... muchas gracias señor.
Mohammed Sufian
5

La forma en que encontré para enviar PNG y transparencia.

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")));
JulienGenoud
fuente
El valor 85 aquí no tiene sentido ya que PNG no tiene pérdidas. La documentación dice: `Algunos formatos, como PNG que no tiene pérdidas, ignorarán la configuración de calidad '
Minhaz
2

Crea una miniatura de video para un video. Puede volver nulo si el video está dañado o el formato no es compatible.

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

Para guardar su mapa de bits en la tarjeta SD, use el siguiente código

Almacenar imagen

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 obtener la ruta para el almacenamiento de imágenes

/** 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;
} 
MashukKhan
fuente
2

Desea guardar el mapa de bits en el directorio de su elección. He creado una biblioteca ImageWorker que permite al usuario cargar, guardar y convertir imágenes de mapas de bits / dibujables / base64.

SDK mínimo: 14

Requisito previo

  • Guardar archivos requeriría el permiso WRITE_EXTERNAL_STORAGE.
  • Recuperar archivos requeriría el permiso READ_EXTERNAL_STORAGE.

Guardar mapa de bits / Drawable / Base64

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

Cargando mapa de bits

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

Implementación

Agregar dependencias

En Project Level Gradle

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

En el nivel de aplicación Gradle

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

Puede leer más en https://github.com/ihimanshurawat/ImageWorker/blob/master/README.md

Himanshu Rawat
fuente
1

Oye, solo dale el nombre a .bmp

Hacer esto:

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**")

parecerá que IM ESTOY ENGAÑANDO ALREDEDOR, pero pruébalo una vez que se guarde en bmp foramt ... Cheers

Mayank Saini
fuente
1

Asegúrese de crear el directorio antes de llamar bitmap.compress:

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

Después de Android 4.4 Kitkat, y a partir de 2017, la cuota de Android 4.4 y menos es aproximadamente del 20% y disminuye, no es posible guardar en la tarjeta SD usando la Fileclase y el getExternalStorageDirectory()método. Este método devuelve la memoria interna de su dispositivo y las imágenes guardadas visibles para cada aplicación. También puede guardar imágenes privadas en su aplicación y eliminarlas cuando el usuario elimina su aplicación conopenFileOutput() método.

A partir de Android 6.0, puede formatear su tarjeta SD como memoria interna pero solo privada para su dispositivo. (Si formatea el auto SD como memoria interna, solo su dispositivo puede acceder o ver su contenido) Puede guardar en esa tarjeta SD usando otras respuestas, pero si desea utilizar una tarjeta SD extraíble, debe leer mi respuesta a continuación.

Debería usar Storage Access Framework para obtener el onActivityResultmétodo de actividad de uri a carpeta para obtener la carpeta seleccionada por el usuario, y agregar permiso persistente recuperable para poder acceder a la carpeta después de que el usuario reinicie el 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);

            }
        }
    }
}

Ahora, guarde la carpeta de guardar en las preferencias compartidas para no pedirle al usuario que seleccione la carpeta cada vez que desee guardar una imagen.

Debe usar DocumentFileclass para guardar su imagen, no Fileo ParcelFileDescriptor, para obtener más información, puede consultar este hilo para guardar la imagen en la tarjeta SD con el compress(CompressFormat.JPEG, 100, out);método y las DocumentFileclases.

Tracio
fuente
1

Algunos dispositivos nuevos no guardan el mapa de bits. Así que expliqué un poco más ...

asegúrese de haber agregado a continuación el permiso

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

y cree un archivo xml bajo el xmlnombre de la carpeta provider_paths.xml

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

y en AndroidManifest bajo

<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>

luego simplemente llame a 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();
    }

dónde

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());
        }
    }

y otros 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;
    }

y

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);
    }
Zar E Ahmer
fuente
¿Tiene más información sobre error handling for PIE devices..y supongo que la recursión getOutputMediaFilepodría ser un bucle infinito si falla la solución?
Raimund Wege
0

Guarde Bitmap en su galería sin comprimir.

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.");
    }
}
Bhavik Nathani
fuente
-1

// | == | Cree un archivo PNG desde un mapa de bits:

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(); }
}

// | == | Obtener Bimap del archivo:

Bitmap getBmjFrmFylFnc(String pthAndFylTtlVar)
{
    return BitmapFactory.decodeFile(pthAndFylTtlVar);
}
Sujay UN
fuente