在Android中从内部存储读取和保存位图/图片

197

我想要做的是将图像保存到手机内存中,而不是SD卡

我该怎么做?

我已经成功地从相机直接将图像加载到我的应用程序中的ImageView中了。

现在我想将此图像从ImageView保存到我的Android设备的内部存储器,并在需要时访问它。

有人可以请指导我如何做到这一点吗?

我对Android还有些陌生,因此,请提供详细的步骤,谢谢。


你好,/data/data/yourapp/app_data/imageDir 这个路径具体在哪里?http://stackoverflow.com/questions/40323126/where-do-i-find-the-saved-image-in-android - Khalil Khalaf
8个回答

378

使用下面的代码将图像保存到内部目录。

private String saveToInternalStorage(Bitmap bitmapImage){
        ContextWrapper cw = new ContextWrapper(getApplicationContext());
         // path to /data/data/yourapp/app_data/imageDir
        File directory = cw.getDir("imageDir", Context.MODE_PRIVATE);
        // Create imageDir
        File mypath=new File(directory,"profile.jpg");

        FileOutputStream fos = null;
        try {           
            fos = new FileOutputStream(mypath);
       // Use the compress method on the BitMap object to write image to the OutputStream
            bitmapImage.compress(Bitmap.CompressFormat.PNG, 100, fos);
        } catch (Exception e) {
              e.printStackTrace();
        } finally {
            try {
              fos.close();
            } catch (IOException e) {
              e.printStackTrace();
            }
        } 
        return directory.getAbsolutePath();
    }

说明:

1.将以给定的名称创建目录。Javadocs用于指示它将在何处创建目录。

2.您需要提供所需保存的图像名称。

要从内部存储器中读取文件,请使用以下代码。

private void loadImageFromStorage(String path)
{

    try {
        File f=new File(path, "profile.jpg");
        Bitmap b = BitmapFactory.decodeStream(new FileInputStream(f));
            ImageView img=(ImageView)findViewById(R.id.imgPicker);
        img.setImageBitmap(b);
    } 
    catch (FileNotFoundException e) 
    {
        e.printStackTrace();
    }

}

1
我该如何从内存中访问我的图像? - usamazf
@BrijeshThakur:你在loadImageFromStorage中传递了哪个路径?如何获取它? - uniruddh
@i-droid:请阅读saveToInternalSorage()方法中的注释。该路径是用于内部存储,您可以在保存时将其保存。请查看该方法返回的directory.getAbsolutePath()。这是文件保存的目录路径。 - Brijesh Thakur
5
你应该在finally块中关闭流,而不是在try块内部关闭。 - Kenn Cal
2
你好,/data/data/yourapp/app_data/imageDir 精确地位于哪里?http://stackoverflow.com/questions/40323126/where-do-i-find-the-saved-image-in-android - Khalil Khalaf
显示剩余15条评论

81
/**
 * Created by Ilya Gazman on 3/6/2016.
 */
public class ImageSaver {

    private String directoryName = "images";
    private String fileName = "image.png";
    private Context context;
    private boolean external;

    public ImageSaver(Context context) {
        this.context = context;
    }

    public ImageSaver setFileName(String fileName) {
        this.fileName = fileName;
        return this;
    }

    public ImageSaver setExternal(boolean external) {
        this.external = external;
        return this;
    }

    public ImageSaver setDirectoryName(String directoryName) {
        this.directoryName = directoryName;
        return this;
    }

    public void save(Bitmap bitmapImage) {
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(createFile());
            bitmapImage.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (fileOutputStream != null) {
                    fileOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @NonNull
    private File createFile() {
        File directory;
        if(external){
            directory = getAlbumStorageDir(directoryName);
        }
        else {
            directory = context.getDir(directoryName, Context.MODE_PRIVATE);
        }
        if(!directory.exists() && !directory.mkdirs()){
            Log.e("ImageSaver","Error creating directory " + directory);
        }

        return new File(directory, fileName);
    }

    private File getAlbumStorageDir(String albumName) {
        return new File(Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_PICTURES), albumName);
    }

    public static boolean isExternalStorageWritable() {
        String state = Environment.getExternalStorageState();
        return Environment.MEDIA_MOUNTED.equals(state);
    }

    public static boolean isExternalStorageReadable() {
        String state = Environment.getExternalStorageState();
        return Environment.MEDIA_MOUNTED.equals(state) ||
                Environment.MEDIA_MOUNTED_READ_ONLY.equals(state);
    }

    public Bitmap load() {
        FileInputStream inputStream = null;
        try {
            inputStream = new FileInputStream(createFile());
            return BitmapFactory.decodeStream(inputStream);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

使用方法

  • 保存:

new ImageSaver(context).
        setFileName("myImage.png").
        setDirectoryName("images").
        save(bitmap);
  • 加载:

  • Bitmap bitmap = new ImageSaver(context).
            setFileName("myImage.png").
            setDirectoryName("images").
            load();
    

    编辑:

    添加了ImageSaver.setExternal(boolean)以支持根据谷歌的示例保存到外部存储。


    14
    这是另一种有用的方法,可以添加到类中:public boolean deleteFile(){ File file = createFile(); return file.delete(); }该方法会创建一个文件并尝试删除它,最后返回删除操作是否成功的布尔值。 - Micro
    当我想分享已保存的图片时,显示“目录未创建”,并且图片崩溃了。你能帮我吗? - A. N
    你能否添加一条关于此代码可用的许可证的声明,以便将其包含在项目中? - Don Park
    3
    @DonPark 不需要担心,StackOverflow 上的任何代码都受到 StackOverflow 的许可证保护,你可以放心使用 :) - Ilya Gazman
    我正在尝试使用这个,但遇到了问题。有关于我发布的这个问题的任何帮助吗?https://stackoverflow.com/questions/51276641/cannot-find-image-stored @IlyaGazman - Lion789
    我想保存位于外部存储中且路径为/storage/emulated/0/Android/data/<PackageName>/files/<FolderName>/xyz.gif的动画gif文件。现在我想要将此文件保存/复制到图像库并在那里显示。如何操作? - Zia Ur Rahman

    33
    今天遇到了这个问题,以下是我的解决方法。 只需使用所需的参数调用此函数。
    public void saveImage(Context context, Bitmap bitmap, String name, String extension){
        name = name + "." + extension;
        FileOutputStream fileOutputStream;
        try {
            fileOutputStream = context.openFileOutput(name, Context.MODE_PRIVATE);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fileOutputStream);
            fileOutputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    

    同样,阅读相同内容,请使用此选项

    public Bitmap loadImageBitmap(Context context,String name,String extension){
        name = name + "." + extension
        FileInputStream fileInputStream
        Bitmap bitmap = null;
        try{
            fileInputStream = context.openFileInput(name);
            bitmap = BitmapFactory.decodeStream(fileInputStream);
            fileInputStream.close();
        } catch(Exception e) {
            e.printStackTrace();
        }
         return bitmap;
    }
    

    1
    好的,当你说你再次编写它时,我想你已经有了位图或原始数据的图像数据,以字节数组的形式。如果您有位图,则可以直接利用上述函数。如果您以字节数组的形式拥有它,请使用以下内容将其转换为位图 Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapdata , 0, bitmapdata .length); 即使它以任何其他形式存在,只需将其转换为位图并使用上述函数即可。 - Anurag
    修改了答案。在我的情况下,出现了一些问题,所以最终我发现应该将扩展名作为参数添加进去。 - Naveed Ahmad
    谢谢你的代码,伙计,但我的问题是它将存储在哪里?有默认路径吗? - Rizwan Ahmed
    它将存储在设备的内部存储器中,该存储器将对您的应用程序保密,并且无法直接从SD卡访问。请通过此链接了解存储选项及其使用方法http://developer.android.com/guide/topics/data/data-storage.html#filesInternal - Anurag
    如果您尝试使用一个圆形图像,并在调用getImageBitmap时添加了一个黑色方块 - 将Bitmap.CompressFormat.JPEG更改为Bitmap.CompressFormat.PNG。 - MorZa
    显示剩余4条评论

    15

    对于 Kotlin 用户,我创建了一个 ImageStorageManager 类,它可以轻松处理图像的保存、获取和删除操作:

    class ImageStorageManager {
        companion object {
            fun saveToInternalStorage(context: Context, bitmapImage: Bitmap, imageFileName: String): String {
                context.openFileOutput(imageFileName, Context.MODE_PRIVATE).use { fos ->
                    bitmapImage.compress(Bitmap.CompressFormat.PNG, 25, fos)
                }
                return context.filesDir.absolutePath
            }
    
            fun getImageFromInternalStorage(context: Context, imageFileName: String): Bitmap? {
                val directory = context.filesDir
                val file = File(directory, imageFileName)
                return BitmapFactory.decodeStream(FileInputStream(file))
            }
    
            fun deleteImageFromInternalStorage(context: Context, imageFileName: String): Boolean {
                val dir = context.filesDir
                val file = File(dir, imageFileName)
                return file.delete()
            }
        }
    }
    

    点击这里了解更多信息。


    您是否必须使用从saveToInternalStorage()接收到的绝对路径才能使用getImageFromInternalStorage()检索它,还是只需要文件名? - Leo DroidCoder
    3
    只需 imageFileName 即可检索它。 - Amiraslan
    2
    我浪费了10个小时在这上面。 - Akshay Sahai
    @Amiraslan在API最小值为20和最大值为29时无法工作,甚至目录也没有创建。 - Devendra Singh

    1

    这段代码支持Android 11+。

    在Fragment / Activity中声明权限结果 我正在使用一个Fragment。

    private val askPermissions =
        registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
            val isGranted = permissions.entries.all {
                it.value == true
            }
    
            if (isGranted) {
                viewModel.saveImageToGallery(requireContext().contentResolver,
                    getString(R.string.my_deshi_qr_code),
                    bitmap)
            } else {
                askForWritePermission()
            }
        }
    

    触发事件

    bindingView.downloadQrButton.setOnClickListener {
            requestPermission()
        }
    private fun requestPermission() {
        val minSDK = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
        val isWritePermissionGranted = (ContextCompat.checkSelfPermission(requireContext(),
            Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) || minSDK
    
        if (!isWritePermissionGranted) {
            askForWritePermission()
        } else {
            viewModel.saveImageToGallery(requireContext().contentResolver,
                getString(R.string.my_deshi_qr_code),
                bitmap)
        }
    }
    
    
    private fun askForWritePermission() {
        askPermissions.launch(listOf(Manifest.permission.WRITE_EXTERNAL_STORAGE).toTypedArray())
    }
    

    Viewmodel

    fun saveImageToGallery(contentResolver: ContentResolver, imageName: String, bitmap: Bitmap?) {
        val imageUri: Uri?
        val contentValues = ContentValues().apply {
            put(MediaStore.MediaColumns.DISPLAY_NAME, "$imageName.jpg")
            put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
            bitmap?.let {
                put(MediaStore.Images.Media.WIDTH, bitmap.width)
                put(MediaStore.Images.Media.HEIGHT, bitmap.height)
            }
        }
    
        imageUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH,
                Environment.DIRECTORY_PICTURES + File.separator.toString() + "YourFolderName")
            MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
        } else {
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI
        }
    
        try {
            val uri = contentResolver.insert(imageUri, contentValues)
            val fos = uri?.let { contentResolver.openOutputStream(it) }
            bitmap?.compress(Bitmap.CompressFormat.JPEG, 100, fos)
            Objects.requireNonNull(fos)
            _showMessage.postValue(Event("Image Saved"))
        } catch (e: Exception) {
            _showMessage.postValue(Event("Image Not Saved \n$e"))
        }
    }
    

    0

    请确保将WEBP作为您的媒体格式,以在保持相同质量的情况下节省更多空间:

    fun saveImage(context: Context, bitmap: Bitmap, name: String): String {
            context.openFileOutput(name, Context.MODE_PRIVATE).use { fos ->
                bitmap.compress(Bitmap.CompressFormat.WEBP, 25, fos)
            }
        return context.filesDir.absolutePath
     }
    

    0

    // 多图检索

     File folPath = new File(getIntent().getStringExtra("folder_path"));
     File[] imagep = folPath.listFiles();
    
     for (int i = 0; i < imagep.length ; i++) {
         imageModelList.add(new ImageModel(imagep[i].getAbsolutePath(), Uri.parse(imagep[i].getAbsolutePath())));
     }
     imagesAdapter.notifyDataSetChanged();
    

    0

    如果您想要遵循Android 10的存储编写实践,请查看此处 如果您只想让图像应用程序特定,请查看此处 例如,如果您想要存储一个仅供您的应用程序使用的图像:

    viewModelScope.launch(Dispatchers.IO) {
                getApplication<Application>().openFileOutput(filename, Context.MODE_PRIVATE).use {
                    bitmap.compress(Bitmap.CompressFormat.PNG, 50, it)
                }
            }
    

    getApplication是一种为ViewModel提供上下文的方法,它是AndroidViewModel的一部分。稍后如果您想阅读它:

    viewModelScope.launch(Dispatchers.IO) {
                val savedBitmap = BitmapFactory.decodeStream(
                    getApplication<App>().openFileInput(filename).readBytes().inputStream()
                )
            }
    

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接