xgqfrms™, xgqfrms® : xgqfrms's offical website of cnblogs! xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!

Node.js 服务端如何实现图片防盗链 All In One

Node.js 服务端如何实现图片防盗链 All In One

How the Node.js server implements image anti-leeching

  1. 无扩图片展名 URL
  2. blob URL 一次性链接
  3. 设置有效期 链接
  4. 禁用缓存
  5. Referrer Policy,referer 禁用 Iframe
  6. CORS 白名单

动态生成的 blob 链接,过一段时间后,刷新页面回自动失效 🚀

image

fetch client

// <img id="img" data-src="http://localhost:3000/image" data-type="image/png" src="" alt="" />
function generatorBlobURL(url, type, img) {
  let xhr = new XMLHttpRequest();
  xhr.open('GET', url);
  xhr.responseType = 'arraybuffer';
  xhr.onload = function(res) {
    let blob = new Blob(
      [xhr.response],
      {'type' : type},
    );
    let urlBlob = URL.createObjectURL(blob);
    img.src = urlBlob;
  };
  xhr.send();
}

window.onload = () => {
  async function test() {
    const img = document.querySelector('#img');
    const url = `${img.dataset.src}`;
    const url = `${img.dataset.type}`;
    const res = await fetch(url).then(res => res.json())
    // console.log(`res`, res, typeof res)
    generatorBlobURL(res.url, type, img);
  };
  test()
}

express.js server

const express = require('express');
const app = express();
// middleware
app.use(function (req, res, next) {
  // allow CORS request ✅
  res.header("Access-Control-Allow-Origin", "*");
  next();
});

app.get('/image', async (req, res) => {
 // random cdn image link
  const url = `https://cdn.xgqfrms.xyz/logo/icon.png`;
  const json = {
    url,
  }
  // 🚀 返回 JSON object
  res.json(json)
});

const defaultPort = 3000;
const port = process.env.PORT || defaultPort

app.listen(port, () => {
  console.log(`app is running on http://localhost:${port}`)
});

// https://cdn.xgqfrms.xyz/logo/icon.png
// http://localhost:3000/image

https://github.com/xgqfrms/learning/issues/24

https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#credentialed_requests_and_wildcards

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Access-Control-Allow-Methods

// express.js set Access-Control-Allow-Origin

blob url (blocked:other)

http://127.0.0.1:5501/iframe.html

http://127.0.0.1:5500/image.html

  1. The blob link is a temporary link, if you refresh the page it will lost. Although you can open it in a new tab of the same browser, but not support opening it in incognito mode.

  2. The reason is in incognito mode or a different browser, the blob link will lose its referer or some cookie message.

  3. blob链接是临时链接刷新页面就会丢失; 虽然您可以在同一浏览器新选项卡中打开它,但不支持隐身模式打开它。

  4. 原因是在隐身模式或不同的浏览器中,blob 链接将丢失引用者某些 cookie 消息。

https://stackoverflow.com/questions/72549170/blob-links-are-blocked

demos

image

http://127.0.0.1:5500/image.html

blob:http://127.0.0.1:5500/a9eecfe3-6a40-46d3-a1d3-d5f848af38e6

image

{
  "url": "https://cdn.xgqfrms.xyz/logo/icon.png"
}

http://localhost:3000/image

server.js


// const app = express();

// app.get('/image', async (req, res) => {
//   const url = 'https://example.com/images/test.jpg';
//   res.send(/**/);  // How do I send the image binary data from the url?
// });


// const request = require('request');
// const axios = require('axios');
// const fetch = require('node-fetch');
const express = require('express');
const app = express();


app.use(function (req, res, next) {
  // JSON parse
  // console.log('req.body', req.body);
  // fix CORS bug ✅
  res.header("Access-Control-Allow-Origin", "*");
  // res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  // res.header("Content-Security-Policy", "connect-src *");
  // res.header("Content-Security-Policy", "connect-src '*'");
  // res.header("Content-Security-Policy", "connect-src 'localhost'");
  // res.header("Content-Security-Policy", "connect-src localhost");
  // Content-Security-Policy: connect-src <source>;
  // Content-Security-Policy: connect-src <source> <source>;
  // res.header('Content-Type', 'application/json');
  // res.setHeader('Content-Type', 'application/json');
  next();
});


// header("Access-Control-Allow-Origin: *");

app.get('/image', async (req, res) => {
  const url = `https://cdn.xgqfrms.xyz/logo/icon.png`;
  // res.write(url)
  // res.send()
  // res.send(url)
  const json = {
    url,
  }
  // ✅ 返回 JSON string
  // res.header('Content-Type', 'application/json');
  // res.json(JSON.stringify(json))
  // 🚀 返回 JSON object
  res.json(json)
  // res.set('Content-Type', 'text/html');
  // res.send(JSON.stringify(url));
  // res.sendFile(url)
});

// app.get('/image', async (req, res) => {
//   // const url = 'https://example.com/images/test.jpg';
//   const url = `https://cdn.xgqfrms.xyz/logo/icon.png`;
//   request({
//     url: url,
//     encoding: null
//   },
//   (err, resp, buffer) => {
//     if (!err && resp.statusCode === 200){
//       res.set("Content-Type", "image/jpeg");
//       res.send(resp.body);
//     }
//   });
// });

const defaultPort = 3000;
const port = process.env.PORT || defaultPort

app.listen(port, () => {
  console.log(`app is running on http://localhost:${port}`)
});

// https://cdn.xgqfrms.xyz/logo/icon.png
// http://localhost:3000/image

<!DOCTYPE html>
<html lang="zh-Hans">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <meta name="author" content="xgqfrms">
    <meta name="generator" content="VS code">
    <title>image</title>
</head>
<body>
    <header>
        <h1>image</h1>
    </header>
    <main>
       <img id="img" data-src="http://localhost:3000/image" src="" alt="" />
    </main>
    <footer>
        <p>copyright&copy; xgqfrms 2022</p>
    </footer>
    <script>

function generatorBlobVideo(url, dom) {
  let xhr = new XMLHttpRequest();
  xhr.open('GET', url);
  xhr.responseType = 'arraybuffer';
  xhr.onload = function(res) {
    let blob = new Blob(
      [xhr.response],
      {'type' : 'image/png'},
    );
    console.log(`blob =`, blob)
    let urlBlob = URL.createObjectURL(blob);
    console.log(`urlBlob =`, urlBlob)
    dom.src = urlBlob;
  };
  xhr.send();
}

window.onload = () => {
  async function test() {
    const dom = document.querySelector('#img');
    const url = `${dom.dataset.src}`;
    const res = await fetch(url, {
      // method: 'GET', // *GET, POST, PUT, DELETE, etc.
      // mode: 'cors', // no-cors, *cors, same-origin
      // cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
      // credentials: 'same-origin',
      // credentials: 'include',
      // method: 'GET',
      // // mode: 'no-cors',
      // mode: 'cors',
      // headers: {
      //   // 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;',
      //   // 'Content-Type': 'text/html',
      //   'Accept': 'application/json',
      //   // 'Content-Type': 'application/json',
      //   "Access-Control-Allow-Origin": "*",
      // },
    }).then(res => res.json())
    // }).then(res => {
    //   console.log(`res =`, res)
    //   // console.log(`text =`, res.text())
    //   // return res.text()
    //   console.log(`text =`, res.json())
    //   return res.json()
    // })
    console.log(`res`, res, typeof res)
    generatorBlobVideo(res.url, dom);
  };
  test()
  // setTimeout(() => {
  //   test()
  // }, 3000);
}

    </script>
</body>
</html>

(🐞 反爬虫测试!打击盗版⚠️)如果你看到这个信息, 说明这是一篇剽窃的文章,请访问 https://www.cnblogs.com/xgqfrms/ 查看原创文章!

blob

https://www.cnblogs.com/xgqfrms/p/16120141.html

refs

https://stackoverflow.com/questions/45696999/fetch-unexpected-end-of-input/77003350#77003350

https://stackoverflow.com/questions/60754335/how-do-i-send-image-data-from-a-url-using-express



©xgqfrms 2012-2021

www.cnblogs.com/xgqfrms 发布文章使用:只允许注册用户才可以访问!

原创文章,版权所有©️xgqfrms, 禁止转载 🈲️,侵权必究⚠️!


posted @ 2023-08-30 13:53  xgqfrms  阅读(115)  评论(3编辑  收藏  举报