3

I am trying to save some photos from a server. I have used some parts that i've found, and it was working on emulator(i was using the directory for the photos as Envinronment.getExternalStorageDirectory). Now, I tried to add the app to my phone, and it won't work. When I try to get the images from memory, I get the error: E/BitmapFactory: Unable to decode stream: java.io.FileNotFoundException: /storage/emulated/0/PictogrameProbleme/20.png: open failed: ENOENT (No such file or directory) which means the photos are not saved there in the first place. This is the code I am running to save and retrieve the photos:

LATEST EDIT: After serious testing and debugging and so on, I came past another issue: Failed to create image decoder with message 'unimplemented'

EDIT: I have also added the required permissions in manifest, plus asking for them at runtime.

EDIT: I have edited the code to save on External memory, but the same error occurs. I have used the logcat to get the LOCATIE, from where the the file should be retrieved, and the logcat give me the location of the file as: 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 ;
    }
}

EDIT: I have added the code that works on emulator, but not on phone.

EDIT: Added permissions. in onCreate I call the function 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);
        }
    }
}

Also in manifest:

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

EDIT: Added the way I get the photos from URL, maybe there's the problem.

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

I declare bitmap at the start of the program.

Costin
  • 194
  • 1
  • 2
  • 16
  • "So I tried to use Envinronment.getDataDirectory" -- you do not have access to that directory. "the problem it's that the photos should save on internal memory" -- your code is for [external storage](https://commonsware.com/blog/2019/10/08/storage-situation-external-storage.html). "maybe someone's not using and SD Card and it won't work" -- external storage is not [removable storage](https://commonsware.com/blog/2019/10/11/storage-situation-removable-storage.html). – CommonsWare Mar 16 '20 at 17:18
  • Did you print both full-paths (when you save) and when you read? Are they the same, is the file actually there? Have you tried other places? What else have you tried? – Martin Marconcini Mar 17 '20 at 09:27
  • @MartinMarconcini yes. from `logcat` : where I save the photo : `D/IMAGINE SALVATA File: imagine: /storage/emulated/0/PictogrameProbleme/20.png` and where I read the image from : `D/LOCATIE: /storage/emulated/0/PictogrameProbleme/20.png` – Costin Mar 17 '20 at 09:29
  • I am testing the app on a S20+, with android 10. Is there anything changed there? – Costin Mar 17 '20 at 09:36
  • is the file actually there, like did you browse the device filesystem? – Martin Marconcini Mar 17 '20 at 11:39
  • No, it is not. The error occurs on `FileOutputStream out = new FileOutputStream(file)` when I try to save the photo first – Costin Mar 17 '20 at 12:23

3 Answers3

2

For Google, "Internal Storage" means storage on the phone specifically for your applications use. Not necessarily what you would typically think of "internal". Files created here are not accessible by other applications.

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.

On the other hand, files stored in Primary External Storage (outside of your app but still part of the phones physical storage (not an SD card)) can be accessed by any other application.

If you'd like to read more, you can check out the Google Documentation on File Storage. Keep in mind that there are some specific permissions you need to request from the user in order to save to primary external storage.

In my application, I am creating a CSV file and storing it on the phones Primary External Storage so it can be read by any other app as necessary.

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

I did this by creating a string with the full file path. android.os.Environment.getExternalStorageDirectory().getAbsolutePath() seems to have worked for my application on the emulator and on the physical device.

Tyler Edmonds
  • 97
  • 1
  • 10
  • I have checked the documenttaion, thanks for that! But the situation is still the same. I get the `ENOENT` error using the `Environment.getExternalStorageDirectory().getAbsolutePath()` – Costin Mar 17 '20 at 09:22
  • You mentioned that you put the permissions in the manifest and asked for permissions grammatically. Can you update your post with the code for those? It sounds like a permissions error still so it would be good if we could review those pieces of code as well. Thanks! – Tyler Edmonds Mar 17 '20 at 14:11
  • I have edited the question and added the permission part – Costin Mar 17 '20 at 15:40
2

I have a hunch your problem might be related to your phone running Android Q. look at my answer to this question. external files directory should not be storage/emulated/0/ but rather storage/emulated/0/Android/data/your.package.name/ try using Context.getExternalFilesDir(null) in order to get your external files directory instead.

as you can see from the documentation of Environment.getExternalStorageDirectory() it is deprecated since API 29 and wont return a valid file path if you are targeting Android Q.

let me know if this helps solve your problem.

quealegriamasalegre
  • 2,887
  • 1
  • 13
  • 35
0

Instead of having all those tasks manually, you might consider using Glide to do that job for you. As you are not intending to store the images in external storage, I think Glide is a useful tool for you to cache the images in the internal storage.

You just have to enable proper diskCacheStarategy while loading your images from the server. Here is a good document where you will have the information of different caching techniques.

Integrating Glide in your project is easy. You will have to add the following in your build.gradle file.

repositories {
  mavenCentral()
  google()
}

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

And then, if you want to load your images from your server and cache the original image in your internal memory, just use the following.

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

I hope that helps!

Reaz Murshed
  • 23,691
  • 13
  • 78
  • 98