Как загрузить изображение без расширения файла с URL-адреса в ImageView в Android

У меня есть этот URL-адрес https://speakyfox-api-qa.herokuapp.com/api/v1/files/be28dcec-4912-4f58-8cb8-12b9b2948fc3. На этом URL-адресе есть изображение PNG в хранилище BLOB-объектов, доступное без каких-либо заголовков (обычно) в REST API. Обычно он открывается из веб-браузера (например, Chrome). Однако любая попытка загрузить его в приложение Java Android для ImageView с треском проваливается. Я пробовал Volley, Picasso, Glide, Retrofit и ручные методы. Я включаю код, который я пробовал. Самое близкое, что у меня есть, выдает это сообщение об ошибке D/skia: --- Failed to create image decoder with message 'unimplemented'. Проблема в том, что тип контента image/png, но я не могу его нигде указать, а поскольку URL-адрес не содержит никакого расширения, его довольно проблематично декодировать. Если вы запустите запрос GET через клиент API (например, Postman), он вернет файл PNG.

Такая же проблема присутствует с этим URL https://speakyfox-api-qa.herokuapp.com/api/v1/files/7b6f4529-256f-43a4-ac45-8613d96c505e. Он содержит короткий файл MP3, который я хочу поместить в объект MediaPlayer.

Прежде чем кричать, что это однострочник, я был бы признателен, если бы вы попытались отобразить изображение в ImageView. Проблема в том, что ссылка предназначена для html, а не непосредственно для данных.

// Getting reference to ImageView object
ImageView imageView = (ImageView)findViewById(R.id.imageView);

// Loading using Picasso
Picasso.get().load("https://speakyfox-api-qa.herokuapp.com/api/v1/files/be28dcec-4912-4f58-8cb8-12b9b2948fc3").into(imageView);

// Loading using Glide
Glide.with(MainActivity.this).load("https://speakyfox-api-qa.herokuapp.com/api/v1/files/be28dcec-4912-4f58-8cb8-12b9b2948fc3").into(imageView);

// Loading using HttpURLConnection/InputStream/Bitmap executed in Async task
java.net.URL url = new java.net.URL("https://speakyfox-api-qa.herokuapp.com/api/v1/files/be28dcec-4912-4f58-8cb8-12b9b2948fc3");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
Bitmap myBitmap = BitmapFactory.decodeStream(input);
imageView.setImageBitmap(myBitmap);

// Using Volley [ImageRequest]
RequestQueue queue = Volley.newRequestQueue(this);
ImageRequest request = new ImageRequest("https://speakyfox-api-qa.herokuapp.com/api/v1/files/be28dcec-4912-4f58-8cb8-12b9b2948fc3", new Response.Listener<Bitmap>() {
        @Override
        public void onResponse(Bitmap response) {
            imageView.setImageBitmap(response);
        }
    }, 0, 0, Bitmap.Config.RGB_565, null){
        /** Passing some request headers* */
        @Override
        public Map getHeaders() {
            HashMap headers = new HashMap();
            headers.put("Content-type","image/png");
            return headers;
        }
queue.add(request);

// Using Volley [NetworkImageView]
NetworkImageView networkImageView = (NetworkImageView) findViewById(R.id.networkImageView);
RequestQueue mRequestQueue = Volley.newRequestQueue(this);
ImageLoader mImageLoader = new ImageLoader(mRequestQueue, new ImageLoader.ImageCache() {
        private final LruCache<String, Bitmap> mCache = new LruCache<String, Bitmap>(10);
        public void putBitmap(String url, Bitmap bitmap) {
            mCache.put(url, bitmap);
        }
        public Bitmap getBitmap(String url) {
            return mCache.get(url);
        }
    });
networkImageView.setImageUrl("https://speakyfox-api-qa.herokuapp.com/api/v1/files/be28dcec-4912-4f58-8cb8-12b9b2948fc3", mImageLoader);

// Using Volley [StringRequest]
RequestQueue queue = Volley.newRequestQueue(this);
StringRequest getRequest = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
        @Override
        public void onResponse(String response) {
            byte[] decodedString = Base64.decode(response, Base64.URL_SAFE);
            Bitmap decodedByte = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
            imageView.setImageBitmap(decodedByte);
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {}
    }) {
        /** Passing some request headers* */
        @Override
        public Map getHeaders() {
            HashMap headers = new HashMap();
            headers.put("Content-type","image/png");
            return headers;
        }
    };
queue.add(getRequest);

Этот код, однако, работает (но уродлив)...

WebView webView = findViewById(R.id.webview);
webView.loadUrl("https://speakyfox-api-qa.herokuapp.com/api/v1/files/7b6f4529-256f-43a4-ac45-8613d96c505e");

Тем не менее, мне нужно заставить его работать с ImageView.

РЕДАКТИРОВАТЬ: я попытался загрузить файл с помощью OkHttp, используя этот код

Request request = new Request.Builder().url(url).addHeader("Content-Type", "image/png").build();

OkHttpClient client = new OkHttpClient();
Call call = client.newCall(request);
call.enqueue(new Callback() {
    public void onResponse(Call call, Response response) throws IOException {
        Log.e("",response.body().string());
    }
    public void onFailure(Call call, IOException e) {}
});

Тело ответа

{"code":{"group":3,"internalCode":200,"statusCode":200},"data":{"type":"Image","imageType":"Whole","name": «Я и ты». :44:20.468276Z"}}


person Bernardo Bellotto    schedule 02.12.2019    source источник
comment
Если вы запустите запрос GET через клиент API (например, Postman), он вернет файл PNG — устанавливает ли веб-служба заголовок ответа HTTP Content-type? Если нет, добавьте это, и это должно решить вашу проблему.   -  person CommonsWare    schedule 03.12.2019
comment
@CommonsWare, Postman не передает никаких специальных заголовков. Я добавил заголовки, но это не работает...   -  person Bernardo Bellotto    schedule 03.12.2019
comment
Почтальон не передает никаких специальных заголовков — я имел в виду не это. Я имел в виду ответ вашего веб-сервиса.   -  person CommonsWare    schedule 03.12.2019
comment
Я бы сказал, загрузите и кэшируйте его, а затем просмотрите миниатюру в вашем изображении.   -  person Ahmed I. Elsayed    schedule 03.12.2019
comment
@CommonsWare, извините, я неправильно понял. Да, он возвращает Content-type : image/png.   -  person Bernardo Bellotto    schedule 03.12.2019


Ответы (3)


Рассматриваемое изображение имеет размер 1,6 МБ в формате PNG и размер 5610 x 3963 пикселей. Особого внимания:

  • В настоящее время нет основного Android-устройства с экраном такого разрешения (или аналогичного разрешения).

  • Это изображение займет около 85 МБ в куче.

Скорее всего, ваша проблема в том, что изображение просто слишком большое и вы не можете выделить блок памяти размером 85 МБ для расшифрованного изображения. Либо:

  • Слегка уменьшите разрешение изображения по каждой оси или

  • Загрузите изображение в файл (например, с помощью OkHttp), затем используйте Subsampling Scale Image View или аналогичная библиотека, которая лучше справляется с отображением больших изображений.

person CommonsWare    schedule 02.12.2019
comment
Я использовал другую ссылку с изображением в низком разрешении (68 КБ - 600x400) speakyfox-api-qa.herokuapp.com/api/v1/files/ Сводится к тем же ошибкам, что и в Picasso/Glide: D/skia: --- SkAndroidCodec:: NewFromStream вернул нуль - person Bernardo Bellotto; 03.12.2019
comment
@BernardoBellootto: Тогда есть что-то странное либо с изображениями, либо с тем, как ваш веб-сервис их обслуживает. У вас все еще будет проблема со слишком большим изображением, даже если вы преодолеете проблему с сетью (или что-то еще). - person CommonsWare; 03.12.2019
comment
Насчет размера согласен. Дело в том, что очень простые GET-запросы отлично работают для iOS с использованием Swift. Файлы хранятся в базе данных в виде больших двоичных объектов в соответствии с кодировщиком сервера. Проблема с загрузкой возникает, когда я пытаюсь использовать разные URL-адреса, которые не заканчиваются на .xxx. Может быть, это проблема, связанная с Android или что-то в этом роде. - person Bernardo Bellotto; 03.12.2019

Проблема в том, что ссылка предназначена для html, а не непосредственно для данных.

Верно. И ImageView не будет отображать html.

Итак, вы знаете причину.

Это html первой ссылки:

<html><head><meta name="viewport" content="width=device-width; height=device-height;"><link rel="stylesheet" href="resource://content-accessible/ImageDocument.css"><link rel="stylesheet" href="resource://content-accessible/TopLevelImageDocument.css"><link rel="stylesheet" href="chrome://global/skin/media/TopLevelImageDocument.css"><title>be28dcec-4912-4f58-8cb8-12b9b2948fc3 (PNG-afbeelding, 5610&nbsp;×&nbsp;3963 pixels) - Geschaald (13%)</title></head><body><img src="https://speakyfox-api-qa.herokuapp.com/api/v1/files/be28dcec-4912-4f58-8cb8-12b9b2948fc3" alt="https://speakyfox-api-qa.herokuapp.com/api/v1/files/be28dcec-4912-4f58-8cb8-12b9b2948fc3" class="transparent shrinkToFit" width="746" height="527"></body></html>

Лучше возьми WebView.

person blackapps    schedule 03.12.2019

Я нашел решение. Я скопировал все заголовки запросов, которые были автоматически добавлены в Postman. У меня осталось всего два. Заголовки заставляют сервер возвращать не html, а само изображение. Мне не удалось заставить его работать с большинством методов, указанных выше, но один из них сработал отлично.


RequestQueue queue = Volley.newRequestQueue(this);
ImageRequest request = new ImageRequest(url, new Response.Listener<Bitmap>() {
    @Override
    public void onResponse(Bitmap response) {
        imageView.setImageBitmap(response);
    }
}, 0, 0, ImageView.ScaleType.CENTER_INSIDE, null, null){
    /** Passing some request headers* */
    @Override
    public Map getHeaders() {
        HashMap headers = new HashMap();
        headers.put("Accept","*/*");
        headers.put("Cache-Control","no-cache");
        return headers;
    }
};
queue.add(request);

Есть еще один конструктор (без ImageView.ScaleType.CENTER_INSIDE), но он помечен как устаревший. Он отлично работает даже с большими изображениями, такими как изображение в указанном URL-адресе (хотя загрузка занимает секунду). Единственная проблема - это предупреждение от OkHttp о том, что соединение утекает.

person Bernardo Bellotto    schedule 04.12.2019