Zapisz mapę bitową w lokalizacji

469

Pracuję nad funkcją pobierania obrazu z serwera WWW, wyświetlania go na ekranie, a jeśli użytkownik chce zachować obraz, zapisz go na karcie SD w określonym folderze. Czy istnieje prosty sposób na zrobienie mapy bitowej i zapisanie jej na karcie SD w wybranym folderze?

Mój problem polega na tym, że mogę pobrać obraz i wyświetlić go na ekranie jako mapę bitową. Jedynym sposobem, w jaki udało mi się znaleźć obraz do określonego folderu, jest użycie FileOutputStream, ale wymaga to tablicy bajtów. Nie jestem pewien, jak przekonwertować (jeśli jest to właściwy sposób) z tablicy bitmapowej na tablicę bajtów, więc mogę użyć FileOutputStream do zapisania danych.

Inną opcją, którą mam, jest użycie MediaStore:

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

Które działa dobrze, aby zapisać na karcie SD, ale nie pozwala na dostosowanie folderu.

Chrispix
źródło
Dokładnie to, co robię w mojej aplikacji. Pobieram duży obraz z serwera WWW, nim manipuluję i ładuję bitmapę bezpośrednio do podglądu obrazu za pośrednictwem mImage.setImageBitmap(_result.getBitmap());mojego onTaskComplete()wywołania zwrotnego. Teraz muszę zezwolić użytkownikom na zapisywanie pliku lokalnie, jeśli chcą, za pomocą menu kontekstowego długie naciśnięcie. Powinienem móc skorzystać z poniższego rozwiązania. Co chcę wiedzieć, czy odkryłeś lepsze podejście do tego?
przewodowy00
Jest na to elegancki sposób: stackoverflow.com/questions/4263375/…
Chepech

Odpowiedzi:

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
źródło
11
Skompresowałem również obraz, ale do 100 procent, a kiedy otrzymuję obraz na płótnie, jest on bardzo mały. jakiegokolwiek powodu?
AZ_
3
@Aizaz Nie zmieni to rozmiaru obrazu, tylko format i (potencjalnie) jakość. Warto również zauważyć, że jakość kompresji 90w powyższym przykładzie nie będzie miała żadnego wpływu podczas zapisywania w formacie PNG, ale zmieni różnicę w plikach JPEG. W przypadku JPEG możesz wybrać dowolną liczbę od 0 do 100.
oracz
1
Należy zauważyć, że zapisanie w ten sposób dla .JPEG ze 100% jakością faktycznie zapisze inny obraz niż oryginał w sieci (zajmie przynajmniej znacznie więcej miejsca), Rozważ alternatywne podejście.
Warpzit,
42
Czy trzeba się ponownie kompresować? Chcę tylko zapisać oryginalny obraz.
Hein du Plessis
2
@HeinduPlessis Nie musisz, ale prawdopodobnie powinieneś. Zapisanie surowej mapy bitowej zajmie znacznie więcej miejsca, w zależności od formatu (na przykład ARGB_4444 vs. ARGB_8888).
irwinb
134

Powinieneś użyć tej Bitmap.compress()metody, aby zapisać bitmapę jako plik. Skompresuje (jeśli pozwala na to format) twój obraz i wrzuci go do OutputStream.

Oto przykład instancji Bitmapy uzyskanej dzięki temu, getImageBitmap(myurl)która może być skompresowana jako JPEG ze stopniem kompresji 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());
Joaquin G.
źródło
@JoaquinG jest jakiś powód, dla którego fOut.flush()nie można go pominąć?
Niklas
@Niklas Myślę, że możesz pominąć kolor.
JoaquinG
1
Powinieneś zmienić sformułowanie z „stopień kompresji 85%” na „stopień jakości 85%” dla mniejszej niejednoznaczności. Zinterpretowałbym „stopień kompresji 85%” jako „15% jakości”, ale parametr int Bitmap.compressokreśla jakość.
Tim Cooke
czy możesz zamieścić metodęgetImageBitmap(myurl)
Aashish
38
outStream = new FileOutputStream(file);

zgłasza wyjątek bez pozwolenia w AndroidManifest.xml (przynajmniej w os2.2):

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
użytkownik996042
źródło
10
Nie, jeśli plik absolutna ścieżka jest ścieżką wewnętrzną?
Blundell,
24

Wewnątrz 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
źródło
7
nazywasz to „pippo.jpg”, ale używasz kompresji PNG
Ralphleon
1
Format kompresji powinien mieć format .JPEG, jeśli chcesz zmienić jakość bitmapy. Jakość nie może być zmieniona w formacie PNG.
Mustafa Güven
13

Niektóre formaty, takie jak PNG, który jest bezstratny, zignorują ustawienie jakości.

shinydev
źródło
2
PNG jest nadal formatem skompresowanym. Czy ustawienie jakości nie zmienia jakości kompresji?
Tamás Barta
Stan dokumentacji (wyróżnienie przeze mnie): Wskazówka do kompresora, 0-100. 0 oznacza kompres dla małych rozmiarów, 100 oznacza kompres dla maksymalnej jakości. Niektóre formaty, takie jak PNG, który jest bezstratny, zignorują ustawienie jakości
Stephan Henningsen
10

Oto przykładowy kod do zapisywania bitmapy do pliku:

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

Teraz wywołaj tę funkcję, aby zapisać mapę bitową w pamięci wewnętrznej.

File newfile = savebitmap(bitmap);

Mam nadzieję, że ci to pomoże. Szczęśliwego życia kodowania.

A-Droid Tech
źródło
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
źródło
1
nie musisz spłukiwać strumienia wyjściowego, jeśli przekazujesz go metodą „kompresuj”. ta metoda zrobi w twoim imieniu.
dharam
7

Dlaczego nie wywołać Bitmap.compressmetody 100 (która brzmi, jakby była bezstratna)?

TofuBeer
źródło
Nawet jeśli zostanie zignorowany, powinien wynosić 100. Jeśli pewnego dnia format kompresji zostanie zmieniony na luźny, wówczas obraz będzie najbardziej pasował do niego luźno. Uwaga: jeśli masz kod, który streszcza to wywołanie, może to być ważniejsze.
ddcruver 20.01.11
2
100% nie jest bezstratne z JPEG, FWIW. Możesz to zweryfikować, wielokrotnie ładując i zapisując mapę bitową.
5

Chciałbym również zapisać zdjęcie. Ale moim problemem (?) Jest to, że chcę go zapisać przed bitmapą, którą narysowałem.

Zrobiłem to tak:

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

 }
użytkownik511895
źródło
twoja metoda zapisywania działa tylko dla mnie .. po marnowaniu kilku godzin .. wielkie dzięki Sir.
Mohammed Sufian
5

Sposób, w jaki znalazłem, aby wysłać PNG i przezroczystość.

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
źródło
Wartość 85 tutaj nie ma sensu, ponieważ PNG jest bezstratny. Dokumentacja mówi - „Niektóre formaty, takie jak PNG, który jest bezstratny, zignorują ustawienie jakości”
Minhaz,
2

Utwórz miniaturę wideo dla wideo. Może zwrócić wartość null, jeśli wideo jest uszkodzone lub format nie jest obsługiwany.

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

Aby zapisać bitmapę w sdcard, użyj następującego kodu

Przechowuj obraz

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

Aby uzyskać ścieżkę do przechowywania obrazów

/** 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
źródło
2

Chcesz zapisać bitmapę w wybranym katalogu. Stworzyłem bibliotekę ImageWorker, która umożliwia użytkownikowi ładowanie, zapisywanie i konwersję obrazów bitmap / drawables / base64.

Min. Zestaw SDK - 14

Warunek wstępny

  • Zapisywanie plików wymagałoby pozwolenia WRITE_EXTERNAL_STORAGE.
  • Pobieranie plików wymaga uprawnień READ_EXTERNAL_STORAGE.

Zapisywanie bitmapy / Drawable / Base64

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

Ładowanie mapy bitowej

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

Realizacja

Dodaj zależności

W gradacji poziomu projektu

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

W gradacji poziomu aplikacji

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

Możesz przeczytać więcej na https://github.com/ihimanshurawat/ImageWorker/blob/master/README.md

Himanshu Rawat
źródło
1

Hej, po prostu podaj nazwę .bmp

Zrób to:

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

będzie to brzmiało, że IM JUŻ WIDZISZ WOKÓŁ, ale spróbuj, gdy zostanie zapisany w bmp foramt..Cheers

Mayank Saini
źródło
1

Upewnij się, że katalog został utworzony, zanim zadzwonisz bitmap.compress:

new File(FileName.substring(0,FileName.lastIndexOf("/"))).mkdirs();
użytkownik5480949
źródło
1

Po Androidzie 4.4 Kitkat, a od 2017 roku udział Androida 4.4 i mniej wynosi około 20% i maleje, nie można zapisywać na karcie SD za pomocą Fileklasy i getExternalStorageDirectory()metody. Ta metoda zwraca pamięć wewnętrzną urządzenia i zapisywane obrazy widoczne dla każdej aplikacji. Możesz także zapisywać obrazy tylko prywatne w swojej aplikacji i zostać usunięte, gdy użytkownik usunie Twoją aplikację za pomocąopenFileOutput() metodą.

Począwszy od systemu Android 6.0, możesz sformatować kartę SD jako pamięć wewnętrzną, ale tylko prywatną dla swojego urządzenia. (Jeśli sformatujesz samochód SD jako pamięć wewnętrzną, tylko twoje urządzenie będzie miało dostęp lub zobaczy jego zawartość) Możesz zapisać na tej karcie SD za pomocą inne odpowiedzi, ale jeśli chcesz użyć wymiennej karty SD, przeczytaj moją odpowiedź poniżej.

Powinieneś użyć Storage Access Framework, aby uzyskać onActivityResultmetodę działania uri do folderu , aby uzyskać folder wybrany przez użytkownika, i dodać trwałe, trwałe uprawnienia, aby móc uzyskać dostęp do folderu po ponownym uruchomieniu urządzenia.

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

            }
        }
    }
}

Teraz zapisz folder zapisu we wspólnych preferencjach, aby nie prosić użytkownika o wybranie folderu za każdym razem, gdy chcesz zapisać obraz.

Powinieneś użyć DocumentFileklasy, aby zapisać obraz, a nie, Filelub ParcelFileDescriptor, aby uzyskać więcej informacji, możesz sprawdzić ten wątek, aby zapisać obraz na karcie SD za pomocą compress(CompressFormat.JPEG, 100, out);metody i DocumentFileklas.

Tracki
źródło
1

Niektóre nowe urządzenia nie zapisują bitmapy, więc wyjaśniłem trochę więcej ...

upewnij się, że dodałeś poniżej uprawnienia

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

i utwórz plik xml pod xmlnazwą folderu ścieżka_ dostawcy.xml

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

oraz w AndroidManifest pod

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

następnie po prostu wywołaj 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();
    }

gdzie

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

i inne metody

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

i

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);
    }
Jawad Zeb
źródło
Czy masz więcej informacji na temat error handling for PIE devices..i myślę, że rekurencja getOutputMediaFilemoże być nieskończoną pętlą, jeśli obejście się nie powiedzie.
Raimund Wege,
0

Zapisz mapę bitową w swojej galerii bez kompresji.

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
źródło
-1

// | == | Utwórz plik PNG z mapy bitowej:

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

// | == | Pobierz Bimap z pliku:

Bitmap getBmjFrmFylFnc(String pthAndFylTtlVar)
{
    return BitmapFactory.decodeFile(pthAndFylTtlVar);
}
Sujay ONZ
źródło