在外部存储中保存图像在模拟器上有效,但在具有相同源代码的物理 phone 上无效

Saving images in external storage works on emulator, but not on physical phone with the same source code

我正在尝试从服务器保存一些照片。我已经使用了一些我发现的部分,并且它正在模拟器上工作(我使用的照片目录为 Envinronment.getExternalStorageDirectory)。现在,我尝试将应用程序添加到我的 phone,但它不起作用。当我尝试从内存中获取图像时,出现错误: E/BitmapFactory: Unable to decode stream: java.io.FileNotFoundException: /storage/emulated/0/PictogrameProbleme/20.png: open failed: ENOENT (No such file or directory) 这意味着照片一开始并没有保存在那里。这是我 运行 保存和检索照片的代码:

最新编辑:经过认真的测试和调试等等,我遇到了另一个问题: Failed to create image decoder with message 'unimplemented'

编辑:我还在 manifest 中添加了所需的 permissions,并在 runtime 中要求它们。

编辑:我编辑了代码以在 External memory 上保存,但出现了同样的错误。我已经使用 logcat 获取 LOCATIE,从那里应该检索文件,logcat 给我文件的位置:storage/emulated/0/...

public static String saveToSdCard(Bitmap bitmap, String filename) {

        String stored = null;

        File sdcard = Environment.getExternalStorageDirectory() ;

        File folder = new File(sdcard.getAbsoluteFile(), "/PictogrameProbleme");//the dot makes this directory hidden to the user
        folder.mkdir();
        File file = new File(folder.getAbsoluteFile(), filename + ".png") ;
        if (file.exists())
            return stored ;

        try {
            FileOutputStream out = new FileOutputStream(file);
            bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
            out.flush();
            out.close();
            stored = "success";
        } catch (Exception e) {
            e.printStackTrace();
        }
        return stored;
    }

    public static File getImage(String imagename) {

        File mediaImage = null;
        try {
            String root = Environment.getExternalStorageDirectory().toString();
            File myDir = new File(root);
            if (!myDir.exists())
                return null;

            mediaImage = new File(myDir.getPath() + "/PictogrameProbleme/"+imagename);
            Log.d("LOCATIE",myDir.getPath() + "/PictogrameProbleme/"+imagename );
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return mediaImage;
    }
    public static boolean checkifImageExists(String imagename)
    {
        Bitmap b = null ;
        File file = ImageStorage.getImage(imagename+".png");
        assert file != null;
        String path = file.getAbsolutePath();

        if (path != null)
            b = BitmapFactory.decodeFile(path);

        if(b == null ||  b.equals(""))
        {
            return false ;
        }
        return true ;
    }
}

编辑:我添加了适用于模拟器的代码,但不适用于 phone。

编辑:添加权限。在 onCreate 我调用函数 requestWritePermission().

 @Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    if(requestCode == WRITE_PERMISSION){
        if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
            Log.d(TAG, "Write Permission Failed");
            Toast.makeText(this, "You must allow permission write external storage to your mobile device.", Toast.LENGTH_SHORT).show();
            finish();
        }
    }
}

private void requestWritePermission(){
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)!=PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},WRITE_PERMISSION);
        }
    }
}

也在manifest

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

编辑:添加了我从 URL 获取照片的方式,可能是问题所在。

private void SalveazaPictograme(final String url, final String imagename) {
     class GetImage extends AsyncTask<Object, Object, Object> {
        String requestUrl = url;
        String imagename_ = imagename;


        @Override
        protected Object doInBackground(Object... objects) {
            try {
                URL url = new URL(requestUrl);
                URLConnection conn = url.openConnection();
                bitmap = BitmapFactory.decodeStream(conn.getInputStream());

            } catch (Exception ex) {
            }
           return null;
        }

        @Override
        protected void onPostExecute(Object o) {
            if (!ImageStorage.checkifImageExists(imagename_)) {
                ImageStorage.saveToSdCard(bitmap, imagename_);
                Log.d("LALA","lalalallalaa");
            }

        }
    }
     GetImage au = new GetImage();
     au.execute();
}

我在程序开始时声明bitmap

您可以考虑使用 Glide 为您完成所有这些任务,而不是手动完成所有这些任务。由于您不打算将图像存储在外部存储中,因此我认为 Glide 是一个有用的工具,可让您将图像缓存在内部存储中。

您只需在从服务器加载图像时启用正确的 diskCacheStarategy。这是一个 good document,您将在其中获得不同缓存技术的信息。

在您的项目中集成 Glide 很容易。您必须在 build.gradle 文件中添加以下内容。

repositories {
  mavenCentral()
  google()
}

dependencies {
  implementation 'com.github.bumptech.glide:glide:4.11.0'
  annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
}

然后,如果您想从服务器加载图像并将原始图像缓存在内存中,只需使用以下命令。

GlideApp  
    .with(context)
      .load(yourImageUrl)
      .diskCacheStrategy(DiskCacheStrategy.SOURCE)
      .into(imageView);

希望对您有所帮助!

对于 Google,"Internal Storage" 表示存储在 phone 上专门供您的应用程序使用。不一定是您通常认为的 "internal"。其他应用程序无法访问此处创建的文件。

App-specific storage: Store files that are meant for your app's use only, either in dedicated directories within an internal storage volume or different dedicated directories within external storage. Use the directories within internal storage to save sensitive information that other apps shouldn't access.

另一方面,任何其他应用程序都可以访问存储在主外部存储(在您的应用程序之外,但仍然是 phone 物理存储(不是 SD 卡)的一部分)中的文件。

如果您想阅读更多内容,可以查看 Google Documentation on File Storage。请记住,您需要向用户请求一些特定权限才能保存到主要外部存储。

在我的应用程序中,我正在创建一个 CSV 文件并将其存储在 phones 主外部存储中,以便任何其他应用程序可以根据需要读取它。

    String baseDir = android.os.Environment.getExternalStorageDirectory().getAbsolutePath();
    String fileName = "CarLeaseReport.csv";
    String filePath = baseDir + File.separator + fileName;

我通过创建一个包含完整文件路径的字符串来做到这一点。 android.os.Environment.getExternalStorageDirectory().getAbsolutePath() 似乎适用于我在模拟器和物理设备上的应用程序。

我有预感你的问题可能与你的 phone 运行 Android 问。看看我对此 的回答。外部文件目录不应该是 storage/emulated/0/ 而是 storage/emulated/0/Android/data/your.package.name/ 尝试使用 Context.getExternalFilesDir(null) 来获取你的外部文件目录。

正如您从 Environment.getExternalStorageDirectory()documentation 中看到的那样,它自 API 29 起已被弃用,并且如果您的目标是 [=22],则不会 return 有效的文件路径=]问

如果这有助于解决您的问题,请告诉我。