Збережіть растрові карти до місця


469

Я працюю над функцією для завантаження зображення з веб-сервера, відображення його на екрані, а якщо користувач бажає зберегти зображення, збережіть його на SD-картці у певній папці. Чи є простий спосіб взяти растровий малюнок і просто зберегти його на SD-карті в папці, яку я вибрав?

Моя проблема полягає в тому, що я можу завантажити зображення, відобразити його на екрані у вигляді растрової карти. Єдиний спосіб, який мені вдалося знайти для збереження зображення в певній папці, - це використовувати FileOutputStream, але для цього потрібен масив байтів. Я не впевнений, як конвертувати (якщо це навіть правильний шлях) з Bitmap в байтовий масив, тому я можу використовувати FileOutputStream для запису даних.

Інший варіант, який я маю - це використовувати MediaStore:

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

Це добре працює для збереження на SD-картці, але не дозволяє налаштувати папку.


Саме те, що я роблю в своєму додатку. Я завантажити велику форму зображення вебов - сервер маніпулювати його і завантажувати зображення безпосередньо на ImageView з допомогою mImage.setImageBitmap(_result.getBitmap());в моєму onTaskComplete()зворотному виклику. Тепер я повинен дозволити користувачам зберігати файл на локальному рівні, якщо вони хочуть за допомогою довгого контекстного меню. Я повинен мати можливість використовувати рішення нижче. Що я хочу знати, хоча ви виявили кращий підхід до цього?
провідний00

Існує спосіб елегантний спосіб зробити це тут: stackoverflow.com/questions/4263375 / ...
Chepech

Відповіді:


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
Я також стиснув зображення, але на 100 відсотків, і коли я отримую своє зображення на полотні, воно дуже мало. будь-яка причина?
AZ_

3
@Aizaz Це не змінить розмір зображення, лише формат і (можливо) якість. Варто також зазначити, що якість стиснення 90у наведеному вище прикладі не матиме жодного ефекту при збереженні у форматі PNG, але змінить якість JPEG. У випадку з JPEG ви можете вибрати будь-яке число між 0 і 100.
Орач

1
Слід зазначити, що збереження цього способу для .JPEG зі 100% якістю насправді збереже зображення, ніж оригінал в Інтернеті (принаймні займає набагато більше місця), розглянемо альтернативний підхід.
Warpzit

42
Чи потрібно повторно компресувати? Я просто хочу зберегти оригінальне зображення.
Hein du Plessis

2
@HeinduPlessis Не треба, але, мабуть, слід. Збереження сирої растрової карти займе набагато більше місця, залежно від формату (наприклад, ARGB_4444 проти ARGB_8888).
irwinb

134

Ви повинні використовувати Bitmap.compress()метод для збереження растрової карти як файлу. Він стисне (якщо використовуваний формат дозволяє) вашій фотографії та підштовхне її до OutputStream.

Ось приклад отримання екземпляра Bitmap, getImageBitmap(myurl)який може бути стиснутий як JPEG зі швидкістю стиснення 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 Якась причина цього fOut.flush()не можна опустити?
Ніклас

@Niklas Я думаю, що ви можете пропустити флеш.
JoaquinG

1
Ви повинні змінити формулювання з "коефіцієнт стиснення 85%" на "рівень якості 85%" для меншої двозначності. Я б інтерпретував "швидкість стиснення 85%" означати "15% якість", але параметр int Bitmap.compressвизначає якість.
Тім Кук

Чи можете ви PLZ розмістити методgetImageBitmap(myurl)
Aashish

38
outStream = new FileOutputStream(file);

викине виключення без дозволу в AndroidManifest.xml (принаймні в os2.2):

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

10
Якщо не ваш файл absolPath - це внутрішній шлях?
Блонделл

24

Всередині 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
ви називаєте це "pippo.jpg", але ви використовуєте стиснення PNG
Ральфлеон,

1
Формат стиснення повинен бути .JPEG, якщо ви хочете змінити якість растрової карти. Якість не можна змінити у форматі PNG.
Мустафа Гювен

13

Деякі формати, як-от PNG без втрат, ігнорують налаштування якості.


2
PNG як і раніше стислий формат. Чи не змінюється настройка якості якості стиснення?
Tamás Barta

Стан документів (виділення мною): Підказка до компресора, 0-100. 0 значить компрес для невеликих розмірів, 100 значення компрес для максимальної якості. Деякі формати, як-от PNG без втрат, ігнорують налаштування якості
Stephan Henningsen

10

Ось зразок коду для збереження растрової карти у файл:

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

Тепер зателефонуйте до цієї функції для збереження растрової карти до внутрішньої пам'яті.

File newfile = savebitmap(bitmap);

Сподіваюся, це допоможе тобі. Щасливе життя кодування.


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
вам не потрібно промивати outStream, якщо ви передаєте його методом "стиснення". цей метод зробить ім'я вас.
дхарам

7

Чому б не назвати Bitmap.compressметод 100 (який звучить як без втрат)?


Незважаючи на те, що вона ігнорується, вона повинна бути 100. Якщо коли-небудь формат стиснення буде змінено на вільно, то зображення буде найбільш точно відповідати тому, що воно вільно. Також зверніть увагу, якщо у вас є код, який резюмує цей виклик, це може бути важливішим.
ddcruver

2
На 100% не втрачається JPEG, FWIW. Ви можете перевірити це, завантажуючи та зберігаючи растрову карту кілька разів.

5

Я також хотів би зберегти малюнок. Але моя проблема (?) Полягає в тому, що я хочу врятувати її від растрової карти, яку малював Айв.

Я зробив це так:

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

 }

ваш метод збереження працює тільки для мене .. після того, як витратити кілька годин .. дякую сер.
Мухаммед Суфіан

5

Шукаю, як надсилати PNG та прозорість.

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

Значення 85 тут не має сенсу, оскільки PNG без втрат. У документації сказано: `Деякі формати, як PNG, який втрачає втрати, ігнорують налаштування якості`
Minhaz

2

Створіть мініатюру відео для відео. Він може повернутись до нуля, якщо відео пошкоджено або формат не підтримується.

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

Щоб зберегти свою растрову карту на sdcard, використовуйте наступний код

Зберігати зображення

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

Щоб отримати шлях для зберігання зображень

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

Ви хочете зберегти Bitmap в каталозі вашого вибору. Я створив бібліотеку ImageWorker, яка дозволяє користувачеві завантажувати, зберігати та конвертувати растрові зображення / малюнки / base64 зображення.

Мінімум SDK - 14

Попередня умова

  • Для збереження файлів потрібен WRITE_EXTERNAL_STORAGE дозвіл.
  • Для отримання файлів потрібен READ_EXTERNAL_STORAGE дозвіл.

Збереження растрових зображень / малювання / Base64

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

Завантаження растрових зображень

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

Впровадження

Додайте залежності

На рівні проекту

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

У рівні програми Gradle

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

Детальніше ви можете прочитати на https://github.com/ihimanshurawat/ImageWorker/blob/master/README.md


1

Гей, просто дайте ім'я .bmp

Зробити це:

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

буде звучати, що МИ ПІДТВЕРДЖУЄТЬСЯ ВСЕ, але спробуйте, як тільки він збережеться в bmp foramt ..


1

Переконайтеся, що каталог створено перед тим, як зателефонувати bitmap.compress:

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

1

Після Android 4.4 Kitkat, а станом на 2017 рік частка Android 4.4 і менше становить приблизно 20% і зменшується, зберегти на SD-картці за допомогою Fileкласу та getExternalStorageDirectory()методу неможливо . Цей метод повертає внутрішню пам'ять пристрою, а зображення зберігаються видимими для кожного додатка. Ви також можете зберігати зображення, які є лише приватними у вашій програмі, та видаляти їх, коли користувач видаляє ваш додаток за допомогоюopenFileOutput() методом.

Починаючи з Android 6.0, ви можете відформатувати SD-карту як внутрішню пам'ять, але лише приватну для вашого пристрою. (Якщо ви форматуєте автомобіль SD як внутрішню пам'ять, лише ваш пристрій може отримати доступ до нього чи побачити його вміст) Ви можете зберегти його на цій SD-картці за допомогою інші відповіді, але якщо ви хочете використовувати знімну SD-карту, прочитайте мою відповідь нижче.

Вам слід скористатися Storage Access Framework Framework, щоб отримати урі до onActivityResultметоду активності папок, щоб отримати вибрану користувачем папку, і додати відновлюваний постійний дозвіл, щоб мати доступ до папки після перезавантаження пристрою.

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

            }
        }
    }
}

Тепер збережіть папку збереження у загальних налаштуваннях, щоб не просити користувача вибирати папку кожного разу, коли ви хочете зберегти зображення.

Ви повинні використовувати DocumentFileклас для збереження свого зображення, а не, Fileабо ParcelFileDescriptorдля отримання додаткової інформації ви можете перевірити цей потік для збереження зображення на SD-картці compress(CompressFormat.JPEG, 100, out);методом та DocumentFileкласами.


1

Деякі нові пристрої не зберігають растрові карти, тому я пояснив трохи більше ..

переконайтеся, що ви додали нижче Дозволу

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

і створити XML-файл під xmlназвою папки provider_paths.xml

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

та в AndroidManifest під

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

тоді просто зателефонуйте 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());
        }
    }

та інші методи

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

і

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

Чи є у вас трохи більше інформації, error handling for PIE devices..і я думаю, що рекурсія в getOutputMediaFileмогла б бути нескінченною петлею, якщо вирішення проблеми не вдасться.
Раймунд

0

Збережіть Bitmap у своїй галереї без стиснення.

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

// | == | Створіть файл PNG з растрової карти:

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

// | == | Отримати Bimap з файлу:

Bitmap getBmjFrmFylFnc(String pthAndFylTtlVar)
{
    return BitmapFactory.decodeFile(pthAndFylTtlVar);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.