浏览器插件官方demo学习(四):图片下载

图片下载

官方示例中提供了这个例子,不过是基于v2版本的。这里我们学习一下实现的原理,开发一个基于v3的demo

官方例子:download_images

鼠标右键,点击检查弹窗窗口
在这里插入图片描述
在这里插入图片描述

问题
查看了一下源码,发现这个demo比较简单。是通过获取页面中的img标签来获取链接的,但是这种方式有个问题就是,图片有可能是相对路径。

相对路径存在一下两种情况:
1、网站的基础路径就是图片的根路径(简单)
2、网站的基础路径不是图片的根路径(复杂)

实现

无论是简单的还是复杂的都需要进行消息传递,通过扩展往页面里注入脚本,由这部分脚本来获取到页面里所有img标签的src属性值。然后再由脚本向扩展发送消息,将这些属性值传递给扩展。
以下内容都是基于react进行开发的,第一次用react开发脚本,应该会有不少理解错误的地方。(在这个过程中,很多实现都是问的new bing,真的是太方便了。我向她描述问题,她给我提供思路)

实际上手后才发现,起始还有很多的小细节。一开始是直接使用document.querySelectorAll('img')来获取图片,但是如果存在iframe标签的话,document.querySelectorAll('img') 是无法获取的iframe里的图片
原因是:

document.querySelectorAll(‘img’) 只能获取当前文档中匹配选择器的元素,而不能获取iframe标签里的元素,因为iframe标签里的内容是另一个文档。如果你想获取iframe标签里的img元素,你需要先获取iframe标签的引用,然后使用它的contentDocument属性来访问它的文档,再使用querySelectorAll(‘img’)方法

因此获取图片应该如下:

export const funaa = () => {
    
    
    // 存储src属性
    let srcArray = []
    // 获取body中所有的image标签的currentSrc属性
    let bodyImages = document.querySelectorAll('img')
    Array.from(bodyImages).forEach(img => {
    
    
        srcArray.push(img.currentSrc)
    })
    // 获取iframes中所有的image标签的currentSrc属性
    let iframes = document.querySelectorAll('iframe')
    Array.from(iframes).forEach(iframe => {
    
    
        // 获取当前iframe标签里的img元素
        let iframeImages = iframe.contentDocument.querySelectorAll('img');
        Array.from(iframeImages).forEach(img => {
    
    
            srcArray.push(img.currentSrc)
        })
    })
    console.log("图片是:", srcArray)
}

为什么是currentSrc而不是src属性

说实话,还真不知道currentSrc属性。自己真的是个大菜逼,还一直沾沾自喜,觉得自己水平还可以。

img标签的src属性和currentSrc属性有一些区别。
src属性是用来指定图片的源地址的,它可以是一个绝对的或相对的URL。
currentSrc属性是用来表示当前显示在img标签里的图片的完整URL的,它是一个只读的属性,不能被修改

currentSrc属性在你使用sizessrcset属性来提供多个图片选项的时候很有用,因为它可以让你知道浏览器选择了哪一个图片。例如,下面的代码提供了一个时钟图片的两个不同尺寸,一个是200px宽,另一个是400px宽。sizes属性用来指定图片在不同视口宽度下的显示大小。你可以通过currentSrc属性来查看浏览器实际选择了哪个图片。

<img src="clock-200.jpg" srcset="clock-200.jpg 200w, clock-400.jpg 400w" sizes="(max-width: 400px) 50vw, 90vw">
<script>
  var img = document.querySelector('img');
  console.log(img.currentSrc); // 打印出当前显示的图片的URL
</script>

上面还在分析如果src属性是相对路径时该如何获取到这个图片的地址呢,现在直接被打脸了。这下也不用考虑了,直接用currentSrc 属性就好了

在这里插入图片描述
除了img标签中存在srccurrentSrc 属性外,video标签中同样存在这两个属性。获取网页中的图片,起始也可以变成获取网页中的视频。

但是获取视频其实还有些别的问题:

  • 视频是个视频流,你需要把每一个流文件给存起来,最后再把这些流文件给合并成一个完整的视频文件。关于视频流,前段时间拿来解决过一个问题,感兴趣的见:前端播放大视频卡顿的解决(m3u8视频流)
  • 上面的问题,真要想解决也是可以解决的。比如手机端的qq浏览器的嗅探功能,可以将视频流下载下来,然后再转成MP4,原理应该差不多(个人觉得)。再复杂的情况应该是对这些流文件给加密,你就算下载下来也没用,你无法进行解密。

下载视频这个如果以后有时间,会专门写一篇文章来介绍。

言归正传,继续来实现图片下载这个功能。获取到图片后我们将这些图片地址给缓存起来,这里使用chrome.storage.local

chrome.storage.local是一个用来存储数据的API,它可以让你在Chrome扩展中保存和读取对象。 它和localStorage 类似,但是有一些区别:

  • chrome.storage.local可以保存任何类型的数据,而localStorage只能保存字符串类型的数据。
  • chrome.storage.local可以在不同的上下文中共享数据,例如background.jspopup.html,而localStorage只能在同一个网页中访问数据。
  • chrome.storage.local有一个存储限制,一般是5MB,除非你申请了unlimitedStorage权限,而``localStorage`的存储限制取决于浏览器和网站。
  • chrome.storage.local是异步的,需要使用回调函数来处理结果,而localStorage是同步的,可以直接返回结果。

可以使用chrome.storage.local.set方法来保存数据,使用chrome.storage.local.get方法来读取数据,使用chrome.storage.local.remove方法来删除数据。还可以使用chrome.storage.onChanged事件来监听数据的变化

  // 保存图片链接
  const [imgList, setImgList] = useState([]);
  // 注入脚本
  const insertScripts = () => {
    
    
    checkPage(async (tab) => {
    
    
      //依赖注入
      const result = await chrome.scripting.executeScript({
    
    
        target: {
    
     tabId: tab.id },
        func: getAllImage,
      });
      //console.log(result);
      // 这个API暂时无用,如果向打开一个新页面展示这些图片时有用
      chrome.storage.local.set('imgs',result[0]?.result || [])
      setImgList(result[0]?.result || []);
    });
  };

渲染这些图片

import React from 'react';
import './img-list.scss';
import {
    
     Checkbox } from 'antd';

const ImgList = (props) => {
    
    
  let img = props.list.map((e) => {
    
    
    return (
      <div key={
    
    e} >
        <img src={
    
    e} alt="图片" className="img-item" />
        <Checkbox value={
    
    e} style={
    
    {
    
    position:'relative',right:'20px',top:'-10px'}}></Checkbox>
      </div>
    );
  });
  return (
      <Checkbox.Group className="img-list">{
    
    img}</Checkbox.Group>
  );
};

export default ImgList;

有点丑,好久没用react,有点生疏
在这里插入图片描述
图片下载

图片下载这里需要用到 chrome插件提供的api,先添加下载权限 downloads,然后直接使用下面的方法即可进行下载。
除此之外还需用到 FileSaverjszipchrome.downloads.download只能根据url进行下载,不能下载文件流。

chrome.downloads.download({
    
    url: 'https://scpic.chinaz.net/Files/pic/pic9/202011/apic28854_s.jpg'});
npm i jszip
npm i file-saver

这里打算进行简单的优化:

  • 单张图片的下载,直接下载
  • 多张图片的下载,将多张图片放到一个压缩文件里将这个压缩文件进行下载
// 转换图片为base64格式
  const getImageBase64 = (url) => {
    
    
    let image = new Image();
    image.crossOrigin = 'Anonymous';
    image.src = url;
    // 创建一个画布
    const canvas = document.createElement('canvas');
    // 让画布的宽高等于图片的宽高
    canvas.width = image.width;
    canvas.height = image.height;
    // 然后在画布上进行画画
    const ctx = canvas.getContext('2d');
    // 开始画图片,1.绘制的对象2.绘制的位置,3绘制的宽高
    ctx.drawImage(image, 0, 0, image.width, image.height);
    return canvas.toDataURL();
  };

  // 将文件打包并下载
  const downloadZip = async (sourceList, zipName) => {
    
    
    const zip = new JSZip(); // 创建一个压缩对象
    const fileFolder = zip.folder(zipName); // 创建 zipName 文件夹
    const fileList = [];
    // 遍历图片数据
    for (let i = 0; i < sourceList.length; i++) {
    
    
      let image = sourceList[i];
      // 路径base64转换
      const url = await getImageBase64(image);
      let filename = image.split('/').pop();
      // 往压缩包里添加图片数据
      fileList.push({
    
    
        name: filename, // 压缩名
        img: url.substring(22), // 去掉base64文件标识,img是文件形式
      });
      // 确保每个数据都遍历完
      if (fileList.length === sourceList.length) {
    
    
        // 确保数据不为空
        if (fileList.length) {
    
    
          for (let k = 0; k < fileList.length; k++) {
    
    
            // 往文件夹中,添加每张图片数据fileFolder文件夹压缩包
            fileFolder.file(fileList[k].name, fileList[k].img, {
    
    
              base64: true,
              // 下面的可以省略
              compression: 'DEFLATE', // STORE:默认不压缩 DEFLATE:需要压缩
              compressionOptions: {
    
    
                level: 9, // 压缩等级1~9    1压缩速度最快,9最优压缩方式
                // [使用一张图片测试之后1和9压缩的力度不大,相差100字节左右]
                // 注意到达这里的时候image的路径必须是带有data:image/png;base64,
              },
            });
          }
          zip
            .generateAsync({
    
    
              // 压缩的结果为blob类型(二进制流),可用作文件上传
              type: 'blob',
            })
            .then((content) => {
    
    
              // 直接在浏览器打成zipName.zip包并下载,saveAs依赖的js是FileSaver.js
              FileSaver.saveAs(content, zipName + '.zip');
            })
            .catch((msg) => {
    
    
              console.log(msg);
            });
        }
      }
    }
  };

效果

单张
在这里插入图片描述
多张
在这里插入图片描述
在这里插入图片描述

下载

下载插件
关注公众号回复关键字:图片下载插件 ,进行下载,下载完成后,将压缩包解压,将解压后的文件夹拖进浏览器的扩展程序里。

下载插件及源码(收费)

关注公众号,回复关键词 图片下载插件源码。会收到一张1元的支付二维码,付款后将付款截图在公众号里发给我。收到付款截图后,我将在6小时以内发你源码。

猜你喜欢

转载自blog.csdn.net/weixin_41897680/article/details/129801109