Сохранение изображений во внешнем хранилище работает на эмуляторе, но не на физическом телефоне с тем же исходным кодом - PullRequest
3 голосов
/ 16 марта 2020

Я пытаюсь сохранить некоторые фотографии с сервера. Я использовал некоторые части, которые я нашел, и он работал на эмуляторе (я использовал каталог для фотографий как Envinronment.getExternalStorageDirectory). Теперь я попытался добавить приложение в свой телефон, и оно не будет работать. Когда я пытаюсь получить изображения из памяти, я получаю ошибку: 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'

РЕДАКТИРОВАТЬ: I также добавили необходимые permissions в manifest, плюс запрос их на 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 ;
    }
}

РЕДАКТИРОВАТЬ: I добавил код, который работает на эмуляторе, но не на телефоне.

РЕДАКТИРОВАТЬ: Добавлены разрешения. в 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 в начале программы.

Ответы [ 3 ]

3 голосов
/ 18 марта 2020

У меня есть подозрение, что ваша проблема может быть связана с вашим телефоном, работающим Android В. Посмотрите на мой ответ на этот вопрос . каталог внешних файлов не должен быть storage/emulated/0/, а скорее storage/emulated/0/Android/data/your.package.name/, попробуйте использовать Context.getExternalFilesDir(null), чтобы вместо этого получить каталог внешних файлов.

, как вы можете видеть из документации из Environment.getExternalStorageDirectory() устарел, начиная с API 29, и не будет возвращать правильный путь к файлу, если вы настроены на Android Q.

, дайте мне знать, если это поможет решить вашу проблему.

2 голосов
/ 16 марта 2020

Для Google «Внутреннее хранилище» означает хранилище на телефоне специально для ваших приложений. Не обязательно то, что вы обычно думаете о «внутреннем». Файлы, созданные здесь, не доступны другим приложениям.

Спецификация приложения c хранилище: Храните файлы, предназначенные только для вашего приложения, либо в выделенных каталогах внутри внутреннего хранилища * тома 1007 *, либо различные выделенные каталоги во внешнем хранилище. Используйте каталоги в внутреннем хранилище для сохранения конфиденциальной информации, к которой другие приложения не должны обращаться.

С другой стороны, файлы хранятся в основном внешнем хранилище (вне вашего приложения, но Тем не менее, часть физической памяти телефона (не SD-карта) может быть доступна любым другим приложением.

Если вы хотите узнать больше, вы можете ознакомиться с Документацией Google по хранению файлов . Имейте в виду, что есть некоторые определенные c разрешения, которые необходимо запрашивать у пользователя для сохранения на первичном внешнем хранилище.

В моем приложении я создаю файл CSV и сохраняю его на первичном внешнем запоминающем устройстве телефона, чтобы его можно было прочитать любым другим приложением при необходимости.

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

Я сделал это, создав строку с полным путем к файлу. android .os.Environment.getExternalStorageDirectory (). GetAbsolutePath (), кажется, работал для моего приложения на эмуляторе и на физическом устройстве.

0 голосов
/ 16 марта 2020

Вместо того, чтобы выполнять все эти задачи вручную, вы можете использовать Glide , чтобы выполнить эту работу за вас. Поскольку вы не собираетесь хранить изображения во внешнем хранилище, я думаю, что Glide - это полезный инструмент для кэширования изображений во внутреннем хранилище.

Вы просто должны включить diskCacheStarategy при загрузке изображений с сервера. Вот хороший документ , где вы будете иметь информацию о различных методах кэширования.

Интегрировать 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);

Надеюсь, это поможет!

...