嵌入式图像预览加载图像速度更快

本文中介绍的嵌入式图像预览(EIP)技术允许我们使用渐进式JPEG,Ajax和HTTP范围请求在延迟加载期间加载预览图像,而无需传输其他数据。 低质量图像预览(LQIP)和基于SVG的变体SQIP是延迟图像加载的两种主要技术。两者的共同之处在于您首先生成低质量的预览图像。这将显示模糊,然后由原始图像替换。如果您可以向网站访问者呈现预览图像而无需加载其他数据,该怎么办? 根据规范,主要使用延迟加载的JPEG文件可以以这样的方式存储包含在其中的数据,即首先显示粗略的然后显示详细的图像内容。在加载(基线模式)期间,不是从上到下构建图像,而是可以非常快速地显示模糊图像,其逐渐变得更清晰和更锐利(渐进模式)。 基线模式(大预览) 渐进模式(大预览) 除了由更快速显示的外观提供的更好的用户体验之外,渐进式JPEG通常也小于其基线编码的对应物。对于大于10 kB的文件,根据雅虎开发团队的Stoyan Stefanov,使用渐进模式时,图像较小的概率为94%。 如果您的网站包含许多JPEG,您会注意到即使是渐进式JPEG也会依次加载。这是因为现代浏览器只允许六个同时连接到域。因此,单独的渐进式JPEG不是为用户提供最快的页面印象的解决方案。在最坏的情况下,浏览器将在开始加载下一个图像之前完全加载图像。 这里提出的想法现在只是从服务器加载渐进式JPEG的这么多字节,您可以快速获得图像内容的印象。稍后,在我们定义的时间(例如,当加载了当前视口中的所有预览图像时),应该加载图像的其余部分,而不再请求已经为预览请求的部分。 使用两个请求加载渐进式JPEG(大预览) 遗憾的是,您无法告诉img属性中的标记应该在什么时间加载多少图像。但是,使用Ajax,只要提供映像的服务器支持HTTP范围请求,这是可能的。 使用HTTP范围请求,客户端可以在HTTP请求标头中通知服务器,所请求文件的哪些字节将包含在HTTP响应中。每个较大的服务器(Apache,IIS,nginx)都支持此功能,主要用于视频播放。如果用户跳到视频的末尾,则在用户最终看到所需部分之前加载完整视频效率不高。因此,服务器仅请求用户请求的时间周围的视频数据,以便用户可以尽可能快地观看视频。 1.创建渐进式JPEG 渐进式JPEG由几个所谓的扫描段组成,每个扫描段包含最终图像的一部分。第一次扫描仅非常粗略地显示图像,而文件后面的图像会向已加载的数据添加越来越多的详细信息,最终形成最终外观。 单个扫描的确切外观由生成JPEG的程序决定。在类似的命令行程序cjpeg依据从mozjpeg项目,甚至可以定义哪些数据这些扫描包含。但是,这需要更深入的知识,这超出了本文的范围。为此,我想参考我的文章“ Finally Understanding JPG ”,它讲授了JPEG压缩的基础知识。在wizard.txt中解释了必须在扫描脚本中传递给程序的确切参数mozjpeg项目。在我看来,默认情况下mozjpeg使用的扫描脚本(七次扫描)的参数是快速渐进结构和文件大小之间的良好折衷,因此可以采用。 要将我们的初始JPEG转换为渐进式JPEG,我们使用jpegtranmozjpeg项目。这是一个对现有JPEG进行无损更改的工具。Windows和Linux的预编译版本可在此处获得:https://mozjpeg.codelove.de/binaries.html。如果您出于安全考虑而喜欢安全地玩,那么最好自己构建它们。 从命令行我们现在创建渐进式JPEG: $ jpegtran input.jpg > progressive.jpg
复制 我们想要构建渐进式JPEG的事实由jpegtran假定,并且不需要明确指定。图像数据不会以任何方式更改。仅改变文件内的图像数据的排列。 理想情况下,应从JPEG中删除与图像外观无关的元数据(例如Exif,IPTC或XMP数据),因为如果元数据解码器位于图像内容之前,则相应的段只能被元数据解码器读取。由于这个原因我们无法将它们移动到文件中的图像数据后面,因此它们已经与预览图像一起传送并相应地放大第一个请求。使用命令行程序,
exiftool您可以轻松删除这些元数据: $ exiftool -all= progressive.jpg 复制 如果您不想使用命令行工具,还可以使用在线压缩服务compress-or-die.com生成不带元数据的渐进式JPEG。 2.确定第一个HTTP范围请求必须加载预览图像的字节偏移量 JPEG文件被分成不同的段,每个段包含不同的组件(图像数据,诸如IPTC,Exif和XMP之类的元数据,嵌入的颜色配置文件,量化表等)。这些段中的每一个都以由十六进制FF字节引入的标记开始。接下来是一个表示段类型的字节。例如,D8完成标记到SOI标记FF D8(图像开始),每个JPEG文件开始使用。 每次扫描开始都由SOS标记(扫描开始,十六进制FF DA)标记。由于SOS标记后面的数据是熵编码的(JPEG使用霍夫曼编码),因此FF C4在SOS段之前还有另一个具有解码所需的霍夫曼表(DHT,十六进制)的段。因此,渐进式JPEG文件中的我们感兴趣的区域由交替的霍夫曼表/扫描数据段组成。因此,如果我们想要显示图像的第一次非常粗略的扫描,我们必须FF C4从服务器请求直到第二次出现DHT段(十六进制)的所有字节。
JPEG文件的结构(
大预览) 在PHP中,我们可以使用以下代码来读取所有扫描到数组所需的字节数: <?php $img = “progressive.jpg”; $jpgdata = file_get_contents($img); $positions = []; $offset = 0; while ($pos = strpos($jpgdata, “\xFF\xC4″, $offset)) { $positions[] = $pos+2; $offset = $pos+2; } 我们必须将2的值添加到找到的位置,因为浏览器只在遇到新标记时才会呈现预览图像的最后一行(如前所述,它包含两个字节)。 由于我们对此示例中的第一个预览图像感兴趣,因此我们找到了正确的位置$positions[1],我们必须通过HTTP范围请求来请求该文件。要请求具有更好分辨率的图像,我们可以使用数组中的稍后位置,例如$positions[3]。   3.创建前端JavaScript代码 首先,我们定义一个img标记,我们给出刚刚评估的字节位置: <img data-src=”progressive.jpg” data-bytes=”<?= $positions[1] ?>”>
复制 与延迟加载库的情况一样,我们不src直接定义属性,因此浏览器在解析HTML代码时不会立即开始从服务器请求图像。 使用以下JavaScript代码,我们现在加载预览图像: var $img = document.querySelector(“img[data-src]”); var URL = window.URL || window.webkitURL; var xhr = new XMLHttpRequest(); xhr.onload = function(){ if (this.status === 206){ $img.src_part = this.response; $img.src = URL.createObjectURL(this.response); } } xhr.open(‘GET’, $img.getAttribute(‘data-src’)); xhr.setRequestHeader(“Range”, “bytes=0-” + $img.getAttribute(‘data-bytes’)); xhr.responseType = ‘blob’; xhr.send();
复制 此代码创建一个Ajax请求,该请求告诉HTTP范围标头中的服务器将文件从开头返回到data-bytes…中指定的位置,而不是更多。如果服务器理解HTTP范围请求,它将以blob的形式返回HTTP-206响应(HTTP 206 =部分内容)中的二进制图像数据,我们可以使用它生成浏览器内部URL createObjectURL。我们将此网址用作src我们的img代码。因此我们加载了预览图像。 我们将blob另外存储在属性中的DOM对象上src_part,因为我们将立即需要这些数据。 在开发人员控制台的网络选项卡中,您可以检查我们是否未加载完整的图像,但只是一小部分。此外,应显示blob URL的加载,大小为0字节。
加载预览图像时的网络控制台(
大预览) 由于我们已经加载了原始文件的JPEG标题,因此预览图像的大小正确。因此,根据应用,我们可以省略img标签的高度和宽度。 替代方案:内嵌加载预览图像 出于性能原因,还可以将预览图像的数据直接作为数据URI传输到HTML源代码中。这节省了传输HTTP头的开销,但base64编码使图像数据增加了三分之一。如果您使用gzip或brotli等内容编码提供HTML代码,则会对此进行相对化处理,但您仍应将数据URI用于小型预览图像。 更重要的是,预览图像立即可用,并且在构建页面时用户没有明显的延迟。 首先,我们必须创建数据URI,然后我们在img标记中使用它src。为此,我们通过PHP创建数据URI,其中此代码基于刚刚创建的代码,该代码确定SOS标记的字节偏移: <?php … $fp = fopen($img, ‘r’); $data_uri = ‘data:image/jpeg;base64,’. base64_encode(fread($fp, $positions[1])); fclose($fp);
复制 创建的数据URI现在直接插入到`img`标签中src: <img src=”<?= $data_uri ?>” data-src=”progressive.jpg” alt=””>
复制 当然,JavaScript代码也必须适应: <script> var $img = document.querySelector(“img[data-src]”); var binary = atob($img.src.slice(23)); var n = binary.length; var view = new Uint8Array(n); while(n–) { view[n] = binary.charCodeAt(n); } $img.src_part = new Blob([view], { type: ‘image/jpeg’ }); $img.setAttribute(‘data-bytes’, $img.src_part.size – 1); </script>
复制 我们不得不通过Ajax请求请求数据,而是立即收到blob,在这种情况下,我们必须自己从数据URI创建blob。为此,我们从不包含图像数据的部分释放数据URI : data:image/jpeg;base64. 我们用atob命令解码剩余的base64编码数据。为了从现在的二进制字符串数据创建blob,我们必须将数据传输到Uint8数组,这确保数据不被视为UTF-8编码的文本。从这个数组中,我们现在可以使用预览图像的图像数据创建二进制blob。 因此,我们不必为此内联版本调整以下代码,我们data-bytes在img标记上添加属性,在前面的示例中包含必须加载图像的第二部分的字节偏移量。 在开发人员控制台的网络选项卡中,您还可以在此处检查加载预览图像不会生成其他请求,而HTML页面的文件大小已增加。
将预览图像加载为数据URI时的网络控制台(
大预览) 加载最终图像 在第二步中,我们在两秒后加载图像文件的其余部分作为示例: setTimeout(function(){ var xhr = new XMLHttpRequest(); xhr.onload = function(){ if (this.status === 206){ var blob = new Blob([$img.src_part, this.response], { type: ‘image/jpeg’} ); $img.src = URL.createObjectURL(blob); } } xhr.open(‘GET’, $img.getAttribute(‘data-src’)); xhr.setRequestHeader(“Range”, “bytes=”+ (parseInt($img.getAttribute(‘data-bytes’), 10)+1) +’-‘); xhr.responseType = ‘blob’; xhr.send(); }, 2000);
复制 在Range标题中,这次我们指定要从预览图像的结束位置到文件末尾请求图像。第一个请求的答案存储在src_partDOM对象的属性中。我们使用两个请求的响应来创建一个新的blob per new Blob(),其中包含整个图像的数据。从此生成的blob URL再次用作srcDOM对象。现在图像已完全加载。 此外,我们现在可以再次检查开发者控制台的网络选项卡中的加载大小。
加载整个图像时的网络控制台(31.7 kB)(
大预览) 原型 在以下URL中,我提供了一个原型,您可以在其中试验不同的参数:
http
//embedded-image-preview.cerdmann.com/prototype/ 可以在此处找到原型的GitHub存储库:
https
//github.com/McSodbrenner/embedded-image-preview

友情提示:点赞熊源码网http://www.dianzanxiong.com点赞熊源码网 » 嵌入式图像预览加载图像速度更快
分享到:
赞(0)
广告位820*80