So, let's say I have a large version of an image that gets displayed only as a thumbnail. Would it be possible to avoid having a separate file for the thumbnail by using a progressive jpeg, stopping its loading when a certain number of scans have been reached, and continue loading only when the user chooses to open it in full?
If so, how can the loading of the image be controlled?
Thanks in advance.
There are 2 approaches:
Prerequisite
Range: bytes=0-1024
means you are requesting only the first 1024 bytesRange
is not valid or not supported, then the server will return the whole image, which is a good natural "fallback"Cross domain requests: if html and images are on different domains
Access-Control-Allow-Headers: "range"
has to be setApache .htaccess
example on image server:
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Headers: "range"
</IfModule>
CROS
settings, the code falls back to set the data-src
attribute to the src
attributeOverview
AJAX
request
Range
header of AJAX
request to how many bytes you want to get backmimeType
to plaintext (so we can base64 encode it later)<img src="data:image/jpeg;base64,...">
)
CROS
settings), then fall back to set the data-src
attribute to the src
attributeThe code
This is built on gaetanoM's awesome answer here: Get Image using jQuery.ajax() and decode it to 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.sstatic.net/QOPRf.jpg" data-bytes="error">
Ellaborating on ProgressiveMonkey's comment, you can easily trim the image data with php, or any other server side programming language.
Overview
Server-side code
<?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);
?>
Examples
Please see here an example of one of our site's logo (Doklist.com)
Please feel free to play along with this url's div
parameter (also please note that my test server may not handle the increased traffic well):
http://shoepimper.com/progressive-thumb.php?div=14
Reading just 1/24th of the filesize and returning it as a whole image:
<img src="http://shoepimper.com/progressive-thumb.php?div=24">
Reading 1/14th of the image:
<img src="http://shoepimper.com/progressive-thumb.php?div=14">
Reading 1/6th of the image:
<img src="http://shoepimper.com/progressive-thumb.php?div=6">
Reading the whole image (1/1th)
<img src="http://shoepimper.com/progressive-thumb.php?div=1">
If you need help to determine whether the image is progressively encoded or not, then use this: http://codepen.io/sergejmueller/full/GJKwv
If you don't have direct access to the images, then you should use a proxy, meaning the structure of the code itself doesn't really change, you just "fopening" a remote file.