将位图保存到应用程序文件夹

5
当我的应用程序首次启动时,它会让用户选择一个个人资料照片。可以通过拍照或从图库中选择完成此操作。
用户获取照片后,必须将其保存在设备的内部存储器中,并在应用程序中用作用户的个人资料照片。
这个过程很好地运行,用户获取了图片并在保存之前显示在ImageView中。但是,我在将图像保存在内部存储器中时遇到了一些问题。我尝试了几种方法,大多数似乎都有效。但是当我尝试时,图片没有被保存,或者至少我找不到保存它的文件夹。
我已经尝试了以下三种方法:
第一种:
File directory = getDir("profile", Context.MODE_PRIVATE);
File mypath = new File(directory, "thumbnail.png");

FileOutputStream fos = null;
try {
    fos = new FileOutputStream(mypath);
    mybitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
    fos.close();
} catch (Exception e) {
    e.printStackTrace();
}

第二点:

ByteArrayOutputStream bytes = new ByteArrayOutputStream();
mybitmap.compress(Bitmap.CompressFormat.PNG, 90, bytes);

FileOutputStream fos = null;
try {
    fos = openFileOutput("thumbnail.png", Context.MODE_PRIVATE);
    fos.write(bytes.toByteArray());
    fos.close();
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

第三点:

ByteArrayOutputStream bytes = new ByteArrayOutputStream();
mybitmap.compress(Bitmap.CompressFormat.PNG, 90, bytes);

File fileWithinMyDir = new File(getFilesDir(), "thumbnail.png");
try {
    FileOutputStream fos = new FileOutputStream(fileWithinMyDir);
    fos.write(bytes.toByteArray());
    fos.close();
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

据说,图像保存在路径:android/data/AppName/app_data/,但那里没有创建文件夹。无论如何,我已经查看了其他文件夹,但是没有找到。 编辑- 在第一种方法中,我发现它会抛出一个异常:
E/SAVE_IMAGE﹕ /data/data/com.example.myapp/app_profile/thumbnail.png: open failed: EISDIR (Is a directory)
java.io.FileNotFoundException: /data/data/com.example.myapp/app_profile/thumbnail.png: open failed: EISDIR (Is a directory)
Caused by: libcore.io.ErrnoException: open failed: EISDIR (Is a directory)

你是否添加了写外部存储权限? - Quick learner
1
@quicklearner 我不需要写入外部存储 - masmic
6个回答

8

尝试了几种方式后,这是最终为我工作的方法:

ContextWrapper cw = new ContextWrapper(getApplicationContext());
File directory = cw.getDir("profile", Context.MODE_PRIVATE);
if (!directory.exists()) {
    directory.mkdir();
}
File mypath = new File(directory, "thumbnail.png");

FileOutputStream fos = null;
try {
    fos = new FileOutputStream(mypath);
    resizedbitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
    fos.close();
} catch (Exception e) {
    Log.e("SAVE_IMAGE", e.getMessage(), e);
}

基本上,需要检查目录(而不是文件)是否存在,如果不存在,则使用mkdir()创建它。


2
Context#getDir() 方法会在文件夹不存在时创建该文件夹,因此调用 mkdir() 方法是多余的(来源:Context.java 文档) - Jeffrey
1
你是怎么把所有这些方法组合起来让整个程序运行的?你有没有从其他人那里复制代码片段?我一直在复制别人的代码,从来不能自己编写出可运行的代码。 - most venerable sir
1
一个问题。我已经保存了它,但现在在哪里可以找到它? - most venerable sir

4
你可以使用这个ImageSaver类将位图图像保存到你的应用文件夹中。 下面是图像保存器类的代码:
public class ImageSaver {
    private String directoryName = "images";
    private String fileName = "image.png";
    private Context context;
    private File dir;
    private boolean external=false;

    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 setDirectory(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);
            if (!directory.exists()){
                directory.mkdir();
            }
        } else {
            directory = new File(context.getFilesDir()+"/"+directoryName);
            if (!directory.exists()){
                directory.mkdir();
            }
        }

        return new File(directory, fileName);
    }

    private File getAlbumStorageDir(String albumName) {
        File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), albumName);
        if (!file.mkdirs()) {
            Log.e("ImageSaver", "Directory not created");
        }
        return file;
    }

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

    public boolean deleteFile() {
        File file = createFile();
        return file.delete();
    }
}

然后,在您从服务器获取位图的活动中,通过使用Glide或Picasso(但您可以使用任何方法),在调用.setDirectory之前,应该设置setExternal:
Bitmap bitmap=........//bitmap from code
    new ImageSaver(this)
            .setFileName("filename.jpg")
             .setExternal(false)//image save in external directory or app folder default value is false
            .setDirectory("dir_name")
            .save(bitmap); //Bitmap from your code

2
请解释一下你的代码,以便其他用户能够理解其功能。谢谢! - Ignacio Ara
我喜欢这个解决方案,但是我不理解这一部分:
  • directory = new File(context.getFilesDir()+"/"+directoryName); *
- LeandroPortnoy
@LeandroPortnoy 这是为了将文件保存在手机目录中。 - Sam Raju
这段代码不会保存到内部文件夹,而是保存到公共图片库中。我可以从你的代码中看出来,如下所示:new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), albumName); - Ismail Osunlana
@BajioDunamis 如您在create file方法中所见,如果external为true,则保存在外部目录中,否则保存在内部目录中。 - Sam Raju
显示剩余2条评论

1

嗯,如果文件夹不存在,你需要创建它。

尝试运行这段代码,看看是否可以解决你的问题:

File parentDestination = saveFile.getParentFile();
    if (!parentDestination.exists()) {
        parentDestination.mkdirs(); //make all the directory structures needed
    }

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> - JozeRi
我需要那个权限才能在内部存储中写入吗? - masmic
是的,您需要获得许可。 - JozeRi
不好意思,但我认为你错了。我终于成功地在内部存储中写入它,而没有添加那个权限。那个权限只适用于在外部存储(SD卡)中写入,而不是设备的内存。在这种情况下需要的权限是:WRITE_INTERNAL_STORAGE。 - masmic
抱歉,“INTERNAL”,是的,我错了。如果我误导了您,混淆了事情,我很抱歉。 - JozeRi

1

我理解您的要求。以下是我测试过并且有效的一些代码示例。基本上是从相机获取图像并将其保存在应用程序存储中,请参考下面的代码。希望这能够帮助到您。祝好!

// 保存图片的代码...

private String saveToInternalSorage(Bitmap bitmapImage) {
    ContextWrapper cw = new ContextWrapper(getApplicationContext());
    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);
        fos.close();
        Editor editor = sharedpreferences.edit();
        editor.putString("saved", "na");
        editor.commit(); 

    } catch (Exception e) {
        e.printStackTrace();
    }
    return directory.getAbsolutePath();
}

//..从存储器中加载图像

private void loadImageFromStorage(String path) {

    try {
        ContextWrapper cw = new ContextWrapper(getApplicationContext());
        File path1 = cw.getDir("imageDir", Context.MODE_PRIVATE);
        File f = new File(path1, "profile.jpg");
        Bitmap b = BitmapFactory.decodeStream(new FileInputStream(f));
        ImageView img = (ImageView) findViewById(R.id.viewImage);
        img.setImageBitmap(b);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }

}

如果您查看我的代码,那是我使用的第一个方法。 - masmic
2
你能指出如何创建SharedPreferences实例以便访问其edit方法吗? - channae

0

尝试这个...

您应该使用Bitmap.compress()方法将位图保存为文件。它会压缩(如果所使用的格式允许)您的图片并将其推送到OutputStream中。

这是一个通过getImageBitmap(myurl)获取的Bitmap实例的示例,可以以85%的压缩率压缩为JPEG:

String path = Environment.getExternalStorageDirectory().toString();
OutputStream fOut = null;
File file = new File(path, "FitnessGirl"+Contador+".jpg"); // the File to save to
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();
fOut.close(); // do not forget to close the stream

MediaStore.Images.Media.insertImage(getContentResolver(),file.getAbsolutePath(),file.getName(),file.getName());

你的路径指向外部存储器,我需要将其保存在内部存储器中。 - masmic

0
首先感谢@masmic。 我使用它在github repos中进行一些搜索并制定了自己的方法。 请注意,我使用Kotlin、协程和Crashlitics,你可以用Java来摆脱所有这些,通过将数据发送到自定义服务器以okhttp的方式发布线程到处理程序。 我的代码可以将位图保存到应用文件夹,即本地存储。读取它是:
package xxxx.data

import android.content.Context
import android.content.ContextWrapper
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
import java.util.Date

interface BitmapDAO {
    suspend fun insertBitmapAndGetRelativePath(bitmapEntity: PhotoEntity): String?

    suspend fun getBitmapFromPath(bitmapEntity: PhotoEntity): Bitmap?

    suspend fun deleteFile(bitmapEntity: PhotoEntity) : Boolean

    suspend fun updateBitmap(bitmapEntity: PhotoEntity): Boolean
}


class BitmapDAOImpl(private val mContext: Context): BitmapDAO {

    companion object{
        //private const val MIME_TYPE_IMAGE:String = "image/*"
        private const val FILE_EXTENSION:String = ".jpeg"
        private const val FOLDER:String = "images"
    }
    private fun convertFromBitMapToByteArray(bitmap: Bitmap) : ByteArray {
        val byteArrayOutputStream = ByteArrayOutputStream()
        bitmap.compress(Bitmap.CompressFormat.JPEG,100,byteArrayOutputStream)
        return byteArrayOutputStream.toByteArray()
    }
    override suspend fun insertBitmapAndGetRelativePath(bitmapEntity: PhotoEntity): String? {

        val bitmap: Bitmap = bitmapEntity.bitmap.let { bitmap ->
            return@let bitmap
        }?: return null

        val productName: String = bitmapEntity.productName.let {
                productName -> return@let productName
        }?: return null

        // Get the context wrapper
        val wrapper = ContextWrapper(mContext)
        val fileName: String = productName + Date().time.toString() +  FILE_EXTENSION
        try {
            /**
             * Caution: On devices that run Android 7.0 (API level 24) or higher,
             * unless you pass the Context.MODE_PRIVATE file mode
             * into openFileOutput(), a SecurityException occurs.
             */
            return withContext(Dispatchers.IO) {
                // Initialize a new file instance to save bitmap object
                var fileDir = wrapper.getDir(FOLDER, Context.MODE_PRIVATE)
                if (!fileDir.exists()) {
                    fileDir.mkdir()
                }
                fileDir = File(fileDir, fileName)
                //with mContext.openFileOutput It gets: EISDIR (Is a directory)
                FileOutputStream(fileDir).use {
                    it.write(convertFromBitMapToByteArray(bitmap))
                }

                //Uri.parse(file.absolutePath)
                return@withContext fileName
            }
        } catch (ex: FileNotFoundException) {
            //REPORT
            Firebase.crashlytics.setCustomKey("Class", "BitmapDAOImpl")
            Firebase.crashlytics.setCustomKey("Method", "insertBitmapAndGetUri")
            Firebase.crashlytics.setCustomKey("UseCase", "saveImage")
            Firebase.crashlytics.recordException(ex)
            Log.e("BitmapDAOImpl FileNotFoundException", ex.message ?: "")
            Log.e("BitmapDAOImpl FileNotFoundException", ex.stackTrace.toString())
        } catch (ex: IOException) {
            //REPORT
            Firebase.crashlytics.setCustomKey("Class", "BitmapDAOImpl")
            Firebase.crashlytics.setCustomKey("Method", "insertBitmapAndGetUri")
            Firebase.crashlytics.setCustomKey("UseCase", "saveImage")
            Firebase.crashlytics.recordException(ex)
            Log.e("BitmapDAOImpl IOException", ex.message ?: "")
            Log.e("BitmapDAOImpl IOException", ex.stackTrace.toString())
        }
        //only for Exception case
        return null
    }

    override suspend fun getBitmapFromPath(bitmapEntity: PhotoEntity): Bitmap? {
        try {
            val wrapper = ContextWrapper(mContext)
            return withContext(Dispatchers.IO) {
                var fileDir = wrapper.getDir(FOLDER, Context.MODE_PRIVATE)
                if (!fileDir.exists()) {
                    fileDir.mkdir()
                }
                fileDir = File(fileDir, bitmapEntity.fileName)
                var payload : Bitmap? = null
                if (fileDir.exists()){
                    FileInputStream(fileDir).use { fileInputStream ->
                        /**
                         * Loading image
                         */
                        payload = BitmapFactory.decodeStream(fileInputStream)
                    }
                }
                return@withContext payload
            }
        } catch (ex: FileNotFoundException) {
            //REPORT
            Firebase.crashlytics.setCustomKey("Class", "BitmapDAOImpl")
            Firebase.crashlytics.setCustomKey("Method", "getBitmapFromUri")
            Firebase.crashlytics.setCustomKey("UseCase", "readImage")
            Firebase.crashlytics.recordException(ex)
            Log.e("BitmapDAOImpl", ex.message ?: "")
            Log.e("BitmapDAOImpl", ex.stackTrace.toString())
        } catch (out: OutOfMemoryError) {
            //REPORT
            Firebase.crashlytics.setCustomKey("Class", "BitmapDAOImpl")
            Firebase.crashlytics.setCustomKey("Method", "getBitmapFromUri")
            Firebase.crashlytics.setCustomKey("UseCase", "readImage")
            Firebase.crashlytics.recordException(out)
            Log.e("BitmapDAOImpl", out.message ?: "")
            Log.e("BitmapDAOImpl", out.stackTrace.toString())
        }

        // If an error has occurred or the album ID is null, then return a default artwork image
        //return BitmapFactory.decodeResource(applicationContext.resources, R.drawable.ic_launcher_foreground)
        //only for Exception case
        return null
    }

    override suspend fun deleteFile(bitmapEntity: PhotoEntity) : Boolean {
        val wrapper = ContextWrapper(mContext)
        try {
            return withContext(Dispatchers.IO) {
                var fileDir = wrapper.getDir(FOLDER, Context.MODE_PRIVATE)
                if (!fileDir.exists()) {
                    fileDir.mkdir()
                }
                fileDir = File(fileDir, bitmapEntity.fileName)
                if(fileDir.exists()){
                    return@withContext fileDir.delete()
                } else {
                    return@withContext false
                }
            }
        } catch (ex: FileNotFoundException) {
            //REPORT
            Firebase.crashlytics.setCustomKey("Class", "BitmapDAOImpl")
            Firebase.crashlytics.setCustomKey("Method", "deleteFile")
            Firebase.crashlytics.setCustomKey("UseCase", "deleteImage")
            Firebase.crashlytics.recordException(ex)
            Log.e("BitmapDAOImpl", ex.message ?: "")
            Log.e("BitmapDAOImpl", ex.stackTrace.toString())
        }

        //only for Exception case
        return false
    }

    override suspend fun updateBitmap(bitmapEntity: PhotoEntity):Boolean {
        val newImage: Bitmap = bitmapEntity.bitmap.let { newImage ->
            return@let newImage
        }?: return false

        val wrapper = ContextWrapper(mContext)

        try {
            return withContext(Dispatchers.IO) {
                var fileDir = wrapper.getDir(FOLDER, Context.MODE_PRIVATE)
                if (!fileDir.exists()) {
                    fileDir.mkdir()
                }
                fileDir = File(fileDir, bitmapEntity.fileName)
                //if(fileDir.exists()) ?? for both case is the same
                FileOutputStream(fileDir).use {
                    it.write(convertFromBitMapToByteArray(newImage))
                }
                return@withContext true
            }
        } catch (ex: Exception) {
            //REPORT
            Firebase.crashlytics.setCustomKey("Class", "BitmapDAOImpl")
            Firebase.crashlytics.setCustomKey("Method", "updateBitmapAndGetUri")
            Firebase.crashlytics.setCustomKey("UseCase", "updateImage")
            Firebase.crashlytics.recordException(ex)
            Log.e("BitmapDAOImpl", ex.message ?: "")
            Log.e("BitmapDAOImpl", ex.stackTrace.toString())
        }
        return false
    }

} //End of class

谢谢。 祝你有美好的一天。

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