1

郑重警告:本文止于技术研究,请勿在自己的生产环境使用他人图片资源。

通常在开发测试环节,一些资源图片会出现防盗链的错误提示,本文就通过前端基础技术,实现基本的图片跨站显示效果。

防盗链的原理:

  1. 服务端通过请求头的request.headers.referer来判断是否是自己资源白名单的请求来源。
  2. 如果referer=null,则无法判断来源,会正常显示图片。

所以基于以上理论,可以给图片创造一个没有referer的请求环境就可以实现了。

解决思路:

通过iframe来实现无referer的请求环境。

实现过程:

  1. 创建一个base64临时资源,供iframe调用
  2. 在临时资源中,请求图片
  3. 图片加载完成后,修改iframe.height=img.height

源码

base64临时资源:

const src = 'http://test.com/test.png';

const html = `data:text/html;base64,${btoa(`<img src="${src}"/>`)}`

<iframe src="html"></iframe>

使用ResizeObserver监听图片高度

由于当前iframe里只有一个图片,所以监听body高度即可(body有默认margin,后面需要清除样式)。(ResizeObserver API)

var ro = new ResizeObserver(entries => {
  for (let entry of entries) {
    const data = {height: entry.contentRect.height};
    window.parent?.postMessage({...data, window: 'parent'}, '*')
    window.top?.postMessage({...data, window: 'top'}, '*')
  }
})
ro.observe(document.body)

window.addEventListener("message", e => {
  if (e.data.window === 'parent') {
      iframe.style.height = e.data.height + 'px'
  }
}, false)

完整代码 (vue3 setup ts)

<script setup lang="ts">
import { onMounted, ref, withDefaults } from 'vue'

interface IProps {
    src: string;
    id?: string
}

const props = withDefaults(defineProps<IProps>(), {});
const iframe = ref(null)

onMounted(() => {
    if (iframe.value) {
        const html = `<style>body{margin:0;}</style>
        <img src="${props.src}" style="display:block"/>
        <script>
            var ro = new ResizeObserver(entries => {
                for (let entry of entries) {
                    const data = {height: entry.contentRect.height, id: "${props.id || props.src}"};
                    window.parent?.postMessage({...data, window: 'parent'}, '*')
                    window.top?.postMessage({...data, window: 'top'}, '*')
                }
            })
            ro.observe(document.body)
        <\/script>`
        iframe.value.src = `data:text/html;base64,${btoa(html)}`
    }

    window.addEventListener("message", e => {
        if (e.data.window === 'parent' && e.data.id === props.src && iframe.value) {
            iframe.value.style.height = e.data.height + 'px'
        }
    }, false)
})
</script>

<template>
    <iframe ref="iframe" style="display: block; border: 0;"></iframe>
</template>

此文为本人原创创新文章,转发请注明来源。


jsoncode
4k 声望786 粉丝

I'm jsoncode


引用和评论

0 条评论