Enregistrer le bitmap à l'emplacement

469

Je travaille sur une fonction pour télécharger une image à partir d'un serveur Web, l'afficher à l'écran et si l'utilisateur souhaite conserver l'image, enregistrez-la sur la carte SD dans un certain dossier. Existe-t-il un moyen facile de prendre un bitmap et de simplement l'enregistrer sur la carte SD dans un dossier de mon choix?

Mon problème est que je peux télécharger l'image, l'afficher à l'écran sous forme de bitmap. La seule façon que j'ai pu trouver pour enregistrer une image dans un dossier particulier est d'utiliser FileOutputStream, mais cela nécessite un tableau d'octets. Je ne sais pas comment convertir (si c'est la bonne façon) de Bitmap en tableau d'octets, donc je peux utiliser un FileOutputStream pour écrire les données.

L'autre option que j'ai est d'utiliser MediaStore:

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

Ce qui fonctionne bien pour enregistrer sur la carte SD, mais ne vous permet pas de personnaliser le dossier.

Chrispix
la source
Exactement ce que je fais dans mon application. Je télécharge un serveur Web de grande image, le manipule et charge le bitmap directement dans une image via via mImage.setImageBitmap(_result.getBitmap());dans mon onTaskComplete()rappel. Je dois maintenant autoriser les utilisateurs à enregistrer le fichier localement s'ils le souhaitent via le menu contextuel de la presse longue. Je devrais pouvoir utiliser la solution ci-dessous. Ce que je veux savoir cependant, avez-vous découvert une meilleure approche à ce sujet?
wired00
Il y a une manière élégante de le faire ici: stackoverflow.com/questions/4263375/…
Chepech

Réponses:

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
la source
11
J'ai également compressé l'image mais à 100% et quand je reçois mon image dans la toile, elle est très petite. n'importe quelle raison?
AZ_
3
@Aizaz Cela ne changera pas la taille de l'image, seulement le format et (potentiellement) la qualité. Il convient également de noter que la qualité de compression 90, dans l'exemple ci-dessus n'aura aucun effet lors de l'enregistrement au format PNG, mais fera une différence pour les JPEG. Dans le cas d'un JPEG, vous pouvez choisir n'importe quel nombre entre 0 et 100.
laboureur
1
Il convient de noter que l'enregistrement de cette façon pour .JPEG avec une qualité de 100% permettra en fait d'économiser une image différente de l'original sur le Web (prendra au moins beaucoup plus d'espace), Envisagez une approche alternative.
Warpzit
42
Doit - on avoir à recompresser? Je veux juste enregistrer l'image d'origine.
Hein du Plessis
2
@HeinduPlessis Pas besoin, mais vous devriez probablement. L'enregistrement du bitmap brut prendra beaucoup plus d'espace, selon le format (ARGB_4444 vs ARGB_8888 par exemple).
irwinb
134

Vous devez utiliser la Bitmap.compress()méthode pour enregistrer un bitmap en tant que fichier. Il compressera (si le format utilisé le permet) votre image et la poussera dans un OutputStream.

Voici un exemple d'une instance Bitmap obtenue via getImageBitmap(myurl)qui peut être compressée en JPEG avec un taux de compression 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
la source
@JoaquinG aucune raison pour cela fOut.flush()n'est-il pas possible de l'omettre?
Niklas
@Niklas Je pense que vous pouvez omettre le vidage.
JoaquinG
1
Vous devriez changer le libellé de "taux de compression de 85%" à "taux de qualité de 85%" pour moins d'ambiguïté. J'interpréterais "taux de compression de 85%" comme signifiant "15% de qualité", mais le paramètre int Bitmap.compressspécifie la qualité.
Tim Cooke
pouvez-vous plz poster la méthodegetImageBitmap(myurl)
Aashish
38
outStream = new FileOutputStream(file);

lèvera une exception sans autorisation dans AndroidManifest.xml (au moins dans os2.2):

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
user996042
la source
10
Pas si votre fichier absoluPath est un chemin interne?
Blundell
24

À l'intérieur 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
la source
7
vous l'appelez "pippo.jpg" mais vous utilisez la compression PNG
Ralphleon
1
le format de compression doit être .JPEG si vous souhaitez modifier la qualité du bitmap. La qualité ne peut pas être modifiée au format PNG.
Mustafa Güven
13

Certains formats, comme le PNG sans perte, ignoreront le paramètre de qualité.

shinydev
la source
2
PNG est toujours un format compressé. Le paramètre de qualité ne modifie-t-il pas la qualité de la compression?
Tamás Barta
L'état des documents (en soulignant par moi): Indice pour le compresseur, 0-100. 0 signifie compresser pour une petite taille, 100 signifie compresser pour une qualité maximale. Certains formats, comme PNG qui est sans perte, ignoreront le paramètre de qualité
Stephan Henningsen
10

Voici l'exemple de code pour enregistrer le bitmap dans un fichier:

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

Appelez maintenant cette fonction pour enregistrer le bitmap dans la mémoire interne.

File newfile = savebitmap(bitmap);

J'espère que cela vous aidera. Bonne vie de codage.

A-Droid Tech
la source
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
la source
1
vous n'avez pas besoin de vider outStream si vous passez cela dans la méthode 'compress'. cette méthode fera l'affaire en votre nom.
dharam
7

Pourquoi ne pas appeler la Bitmap.compressméthode avec 100 (ce qui semble être sans perte)?

TofuBeer
la source
Même s'il est ignoré, il doit être égal à 100. Si un jour le format de compression est changé en un peu lâche, l'image correspondra le plus étroitement à ce qu'il est lâche. Notez également que si vous avez du code qui résume cet appel, cela peut être plus important.
ddcruver
2
100% n'est pas sans perte avec JPEG, FWIW. Vous pouvez le vérifier en chargeant et en enregistrant le bitmap à plusieurs reprises.
5

Je voudrais également enregistrer une photo. Mais mon problème (?) Est que je veux l'enregistrer à partir d'un bitmap que j'ai dessiné.

Je l'ai fait comme ça:

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

 }
user511895
la source
votre méthode de sauvegarde ne fonctionne que pour moi .. après avoir perdu quelques heures .. merci beaucoup monsieur.
Mohammed Sufian
5

La façon dont j'ai trouvé pour envoyer du PNG et de la transparence.

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
la source
La valeur 85 ici n'a pas de sens car le PNG est sans perte. La documentation dit - «Certains formats, comme PNG qui est sans perte, ignoreront le paramètre de qualité»
Minhaz
2

Créez une vignette vidéo pour une vidéo. Il peut retourner null si la vidéo est corrompue ou si le format n'est pas pris en charge.

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

Pour enregistrer votre bitmap dans sdcard, utilisez le code suivant

Image du magasin

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

Pour obtenir le chemin du stockage d'images

/** 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
la source
2

Vous souhaitez enregistrer Bitmap dans le répertoire de votre choix. J'ai créé une bibliothèque ImageWorker qui permet à l'utilisateur de charger, enregistrer et convertir des images bitmaps / drawables / base64.

SDK min - 14

Prérequis

  • L'enregistrement de fichiers nécessiterait l'autorisation WRITE_EXTERNAL_STORAGE.
  • La récupération de fichiers nécessiterait l'autorisation READ_EXTERNAL_STORAGE.

Enregistrement Bitmap / Drawable / Base64

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

Chargement du bitmap

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

la mise en oeuvre

Ajouter des dépendances

Au niveau du projet Gradle

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

Au niveau de l'application Gradle

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

Vous pouvez en savoir plus sur https://github.com/ihimanshurawat/ImageWorker/blob/master/README.md

Himanshu Rawat
la source
1

Hé, donnez simplement le nom à .bmp

Faites ceci:

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

ça sonnera que IM JUSTE FOOLING AUTOUR, mais essayez-le une fois qu'il sera enregistré dans bmp foramt..Cheers

Mayank Saini
la source
1

Assurez-vous que le répertoire est créé avant d'appeler bitmap.compress:

new File(FileName.substring(0,FileName.lastIndexOf("/"))).mkdirs();
user5480949
la source
1

Après Android 4.4 Kitkat, et à partir de 2017, la part d'Android 4.4 et moins est d'environ 20% et diminue, il n'est pas possible d'enregistrer sur la carte SD en utilisant la Fileclasse et la getExternalStorageDirectory()méthode. Cette méthode renvoie la mémoire interne de votre appareil et les images enregistrées visibles à chaque application. Vous pouvez également enregistrer des images uniquement privées pour votre application et à supprimer lorsque l'utilisateur supprime votre application avec la openFileOutput()méthode.

À partir d'Android 6.0, vous pouvez formater votre carte SD en tant que mémoire interne mais uniquement privée sur votre appareil. (Si vous formatez la voiture SD en mémoire interne, seul votre appareil peut accéder ou voir son contenu) Vous pouvez enregistrer sur cette carte SD en utilisant d'autres réponses mais si vous souhaitez utiliser une carte SD amovible, vous devriez lire ma réponse ci-dessous.

Vous devez utiliser Storage Access Framework pour obtenir la onActivityResultméthode d'activité uri to folder pour obtenir le dossier sélectionné par l'utilisateur, et ajouter une autorisation persistible de récupération pour pouvoir accéder au dossier après que l'utilisateur a redémarré l'appareil.

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

            }
        }
    }
}

Maintenant, enregistrez le dossier d'enregistrement dans les préférences partagées pour ne pas demander à l'utilisateur de sélectionner le dossier chaque fois que vous souhaitez enregistrer une image.

Vous devez utiliser la DocumentFileclasse pour enregistrer votre image, pas Fileou ParcelFileDescriptor, pour plus d'informations, vous pouvez vérifier ce fil pour enregistrer l'image sur la carte SD avec la compress(CompressFormat.JPEG, 100, out);méthode et les DocumentFileclasses.

Thrace
la source
1

Certains nouveaux appareils n'enregistrent pas le bitmap J'ai donc expliqué un peu plus ..

assurez-vous que vous avez ajouté ci-dessous l'autorisation

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

et créez un fichier xml sous xmlle nom de dossier provider_paths.xml

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

et dans AndroidManifest sous

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

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

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

et autres méthodes

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

et

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
la source
Avez-vous plus d'informations sur error handling for PIE devices..et je suppose que la récursivité getOutputMediaFilepourrait être une boucle infinie si la solution de contournement échoue.
Raimund Wege
0

Enregistrez Bitmap dans votre galerie sans compression.

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
la source
-1

// | == | Créez un fichier PNG à partir de 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(); }
}

// | == | Obtenez Bimap à partir d'un fichier:

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