Предотвратить полную загрузку прогрессивного jpeg

Итак, допустим, у меня есть большая версия изображения, которое отображается только в виде эскиза. Можно ли избежать отдельного файла для эскиза, используя прогрессивный jpeg, останавливая его загрузку при достижении определенного количества сканирований и продолжая загрузку только тогда, когда пользователь решит открыть его полностью?

Если да, то как можно контролировать загрузку изображения?

Заранее спасибо.


person ProgressiveMonkey    schedule 05.11.2015    source источник
comment
Если у вас нет контроля над декодером, просто обрежьте файл, чтобы включить только первое сканирование, и вы получите только значения DC (размер 1/8) для вашего изображения.   -  person BitBank    schedule 06.11.2015
comment
Я хочу сделать то же самое. Как вы имеете в виду обрезать файл? Не могли бы вы расширить свой комментарий до возможного ответа?   -  person michael    schedule 01.12.2015
comment
Кажется, я понял это не в том направлении: сервер должен прекратить отправку данных, а не клиент. Все еще не уверен, как это сделать.   -  person ProgressiveMonkey    schedule 09.12.2015
comment
Пожалуйста, смотрите мой ответ ниже, я обновил его, чтобы включить решение на стороне клиента и на стороне сервера.   -  person Viktor Tabori    schedule 05.05.2017


Ответы (2)


Есть 2 подхода:

1. Клиентская сторона тяжелая

Необходимое условие

  • the image server has to support the range HTTP header
    • eg. Range: bytes=0-1024 means you are requesting only the first 1024 bytes
  • you have to know in advance how many bytes you want to request
    • you can say 1/8th of full size, if you push that value from server side
    • поэтому точный номер байта должен быть известен на стороне клиента
  • если Range недействителен или не поддерживается, то сервер вернет изображение целиком, что является хорошим естественным «запасным вариантом».

Междоменные запросы: если HTML и изображения находятся в разных доменах.

  • Access-Control-Allow-Headers: "range" должен быть установлен

Пример Apache .htaccess на сервере образов:

<IfModule mod_headers.c>
    Header set Access-Control-Allow-Origin "*"
    Header set Access-Control-Allow-Headers: "range"
</IfModule>
  • Если браузеру не удается выполнить запрос из-за неправильных настроек CROS, код возвращается, чтобы установить атрибут data-src в атрибут src.

Обзор

  1. request image with an AJAX request
    • set Range header of AJAX request to how many bytes you want to get back
    • установите mimeType в открытый текст (чтобы мы могли позже закодировать его в base64)
  2. Base64 encode data and set it to image's src attribute (<img src="data:image/jpeg;base64,...">)
    • beware for larger images this can be quite heavy on the client
  3. Если установка атрибута src не удалась по какой-либо причине (например, из-за неправильных настроек CROS), вернитесь назад, чтобы установить атрибут data-src в атрибут src.

Код

Это основано на замечательном ответе gaetanoM здесь: Получить изображение с помощью jQuery.ajax() и декодировать его в base64

// for each img, which has data-src and data-bytes attributes
$('img[data-src][data-bytes]').each(function(i,e){
	$.ajax({
	    url: $(e).data('src'), // url of image
	    type: 'GET',
	    headers: {
	    	'Range':'bytes=0-'+$(e).data('bytes') // Range header, eg. Range: bytes=0-1024
	    },
	    mimeType: "text/plain; charset=x-user-defined"
	}).done(function( data, textStatus, jqXHR ) {
	    $(e).attr('src', 'data:image/jpeg;base64,' + base64encode(data)); // on success we set the base64 encoded data to the image's src attribute
	}).always(function(){
	    // if setting the src failed for whatever reason, we fall back to set the data-src attribute to src attribute
	    if(!$(e).attr('src'))
	        $(e).attr('src', $(e).data('src'));
	});
});

function base64encode(str) {
    var CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    var out = "", i = 0, len = str.length, c1, c2, c3;
    while (i < len) {
        c1 = str.charCodeAt(i++) & 0xff;
        if (i == len) {
            out += CHARS.charAt(c1 >> 2);
            out += CHARS.charAt((c1 & 0x3) << 4);
            out += "==";
            break;
        }
        c2 = str.charCodeAt(i++);
        if (i == len) {
            out += CHARS.charAt(c1 >> 2);
            out += CHARS.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
            out += CHARS.charAt((c2 & 0xF) << 2);
            out += "=";
            break;
        }
        c3 = str.charCodeAt(i++);
        out += CHARS.charAt(c1 >> 2);
        out += CHARS.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
        out += CHARS.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
        out += CHARS.charAt(c3 & 0x3F);
    }
    return out;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- total filesize is 35 933 bytes -->
<img data-src="http://shoepimper.com/doklist.com-logo.jpg" data-bytes="1900">
<img data-src="http://shoepimper.com/doklist.com-logo.jpg" data-bytes="2500">
<img data-src="http://shoepimper.com/doklist.com-logo.jpg" data-bytes="5600">

<!-- if data-bytes are erroneous the server will return the whole image -->
<img data-src="http://shoepimper.com/doklist.com-logo.jpg" data-bytes="error">
<!-- if CROS fails, then it falls back to set the data-src attribute to the src attribute -->
<img data-src="https://i.stack.imgur.com/QOPRf.jpg" data-bytes="error">

2. Тяжело на стороне сервера

Разрабатывая комментарий ProgressiveMonkey, вы можете легко обрезать данные изображения с помощью php или любого другого языка программирования на стороне сервера.

Обзор

Код на стороне сервера

<?php
    $div = isset($_GET['div']) && intval($_GET['div'])>1 ? intval($_GET['div']) : 1; // what fraction of the image shall we return
    $img = 'doklist.com-logo.jpg';
    $size = round(filesize($img) / $div); // calculating the size in bytes what we return

    // setting the headers
    header("Content-Type: image/jpeg");
    header("Content-Length: $size");

    $fp = fopen($img, 'r');
        echo fread($fp, $size); // returning the necessary amount of bytes
    fclose($fp);
?>

Примеры

Посмотрите здесь пример одного из логотипов нашего сайта (Doklist.com)

Пожалуйста, не стесняйтесь играть с параметром div этого URL-адреса (также обратите внимание, что мой тестовый сервер может плохо справляться с возросшим трафиком): http://shoepimper.com/progressive-thumb.php?div=14

Чтение только 1/24 размера файла и возврат его как целого изображения: <img src="http://shoepimper.com/progressive-thumb.php?div=24"> введите здесь описание изображения

Чтение 1/14 изображения: <img src="http://shoepimper.com/progressive-thumb.php?div=14"> введите здесь описание изображения

Чтение 1/6 изображения: <img src="http://shoepimper.com/progressive-thumb.php?div=6"> введите здесь описание изображения

Чтение всего изображения (1/1) <img src="http://shoepimper.com/progressive-thumb.php?div=1"> введите здесь описание изображения

Если вам нужна помощь, чтобы определить, кодируется ли изображение прогрессивно или нет, используйте это: http://codepen.io/sergejmueller/full/GJKwv

Если у вас нет прямого доступа к изображениям, вам следует использовать прокси, то есть структура самого кода на самом деле не меняется, вы просто «открываете» удаленный файл.

person Viktor Tabori    schedule 04.05.2017

Это было бы возможно. Вам просто нужно изменить декодер, чтобы сделать это.

Вы можете контролировать загрузку, пока у вас есть доступ к потоку данных.

person user3344003    schedule 06.11.2015