There are 2 approaches:
1. Client-side heavy
Prerequisite
- 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
- so the exact byte number should be known in the client side
- if the
Range
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 set
Apache .htaccess
example on image server:
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Headers: "range"
</IfModule>
- If the browser fails to make the request due to improper
CROS
settings, the code falls back to set the data-src
attribute to the src
attribute
Overview
- request image with an
AJAX
request
- set
Range
header of AJAX
request to how many bytes you want to get back
- set the
mimeType
to plaintext (so we can base64 encode it later)
- 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
- If setting the src attribute fails for whatever reason (eg. improper
CROS
settings), then fall back to set the data-src
attribute to the src
attribute
The 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">
2. Server-side heavy
Ellaborating on ProgressiveMonkey's comment, you can easily trim the image data with php, or any other server side programming language.
Overview
- server side: trim the image data based on an url parameter
- client side: change that url parameter as you wish
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.