iconfont图标库 一键下载工具开发记录

iconfont图标库 一键下载工具开发记录

Github源码地址:https://github.com/yttrium2016/iconfontTools

软件下载地址:发布地址

前言

最近自己想自己制作个App在收集图标 给自己开发使用。

于是找了很有名的[图标库]去找图标但是本来觉得很容易的一件事没想到困难重重,不过作为一个免费的信息提供者,阿里巴巴图标库已经做到了很好很好了.

遇到的几个问题

我是用于App的开发,我对svg图片的使用不是很明白,所以我选择简单的下载PNG格式的图片加入自己项目中,从中发现了几个问题

随便找的一个图标库,我想下载对应的所有图标

第一步,登录 (Github授权,微博授权,内部员工帐号)

 程序员都有Github帐号的吧登录应该不成问题。

第二步,点击每个图片下载。(放弃)


真的是太麻烦了。几十个图标要点几十次才下载完成,耗时耗力。

第三步,批量下载 (可以考虑)

我发现可以通过把喜欢的图片加入购物车来批量下载。



本来觉得这个完美的解决了我的需求。随后发现了一个很坑爹的地方。

没错,当图标个数大于20的时候就会不让下载,也就是当一个图标库里面图标个数大于20的时候你要下载图标的时候就要

  1. 先用鼠标把前20个图标加入购物车
  2. 点击购物车去下载对应图标
  3. 在把购物车清空
  4. 在点接下来的20个图标加入购物车
  5. 在下载对应的图标
  6. ……
  7. 最后下载的一堆压缩包 解压复制出来。

太麻烦了,对我们来说只是想下载40-50个图标 却要在网页上反复操作很多很多次,作为一个创作型程序员来说,有需求,有问题,就让我们用程序来解决问题。

准备开发iconfont图标下载工具


根据上面的想法,我萌生了开发一个自己使用的小工具的想法.

功能设计:

  1. 首先,输入网址(编号)自动获取所有当个库中所有图标.
  2. 使用批量下载,根据获取的图标个数 进行(num/20)次下载 下载完所有图标
  3. 解压所有下载完成后的压缩包
  4. 把所有的解压完的图标复制到统一文件夹
  5. 清除多余的无用缓存文件
  6. 最好有可以设置的下载参数的配置
  7. 其他可以优化的逻辑..

本人使用的技术栈是Java,所以用Java的代码编写了对应的这个小工具.其实主要是记录下开发的思路,有了之后用什么代码实现而已.

1.根据ID或者网页获取一个库中的所有图标.


根据对网页的分析得到,对应的图标都是页面请求完成后通过JS生成的,按F12通过网页的请求分析得到

通过调用接口获取到了对应的图标列表信息



很意外的很轻松的获取了每个图标的详细信息.

接口整理

  URL地址:https://www.iconfont.cn/api/collection/detail.json

  请求必填参数:id

  该方法不需要登录验证.

Java代码实现

String url = "https://www.iconfont.cn/api/collection/detail.json?id=" + cid;
Connection.Response response;
try {
    response = Jsoup.connect(url).ignoreContentType(true).execute();
    String json = response.body();// 获取所有的图标信息
} catch (IOException e) {
    throw new RuntimeException("获取出错" + e.getMessage());
}

使用了Jsoup作 HTML解析器 使用还是很方面的,做简单的爬虫,页面获取工作还是很好的.

2. 将所有的图标下载下来(难点)


难点1 下载接口的参数分析

还是通过F12点击下载,查看得到下载的接口为

接口整理

  URL地址: https://www.iconfont.cn/api/icon/downloadIcon

  请求参数:

    type: 下载图标类型

      png:png图标类型

      svg:svg图标类型

      eps:ai图标类型

    ids: 下载图标的单个ID的列表分隔符(|-1,)

    color: 下载图标的颜色(好像针对单色图标)

    size: 下载图标的大小

    ctoken: 下面再说

分析完看完后非常兴奋,马上写了下载的的代码

String url = "https://www.iconfont.cn/api/icon/downloadIcon";

Map<String, String> data = new HashMap<>();
data.put("type", type);
data.put("ids", idStr);
data.put("color", color);
data.put("size", size);
data.put("ctoken", ctoken);

Connection.Response response = Jsoup.connect(url).data(data).ignoreContentType(true).execute();
FileOutputStream out = (new FileOutputStream(new File("d://temp.zip")));
out.write(response.bodyAsBytes());
out.close();

但是没有想想的那么顺利下载下来

提示我没有登录权限,无法下载.

难点2 登录状态的验证

继续分析,我写的代码请求和网页请求的区别在哪呢

经常测试发现,这个图标下载的网站就算你关闭了浏览器,重启电脑,下次打开的时候你还会是已经授权登录的状态,并不需要重新的进行登录授权的操作.所以得出结论登录的信息存在本地.

信息来源于百度

此处用于登录的信息是保存在cookie里

信息来源于百度

每次请求的时候会把本地的cookie放到Request请求中.用于身份识别(这个是我自己的猜想的具体实现原理尚不明确)

结合上一步 代码修改为

String url = "https://www.iconfont.cn/api/icon/downloadIcon";

Map<String, String> data = new HashMap<>();
data.put("type", type);
data.put("ids", idStr);
data.put("color", color);
data.put("size", size);
data.put("ctoken", ctoken);

Map<String, String> cookies = new HashMap<>();
...

Connection.Response response = Jsoup.connect(url).data(data).cookies(cookies).ignoreContentType(true).execute();
FileOutputStream out = (new FileOutputStream(new File("d://temp.zip")));
out.write(response.bodyAsBytes());
out.close();

成功调用接口下载到本地成功

3-7 一些简单的文件复制拷贝逻辑实现


直接上部分源码了(PS:有些代码大部分来源于网上百度搬运所得)

1.压缩包解压

/**
 * zip解压
 *
 * @param srcFile     zip源文件
 * @param destDirPath 解压后的目标文件夹
 * @throws RuntimeException 解压失败会抛出运行时异常
 */
public static void unZip(File srcFile, String destDirPath) throws RuntimeException {
    long start = System.currentTimeMillis();
    // 判断源文件是否存在
    if (!srcFile.exists()) {
        throw new RuntimeException(srcFile.getPath() + "所指文件不存在");
    }
    // 开始解压
    ZipFile zipFile = null;
    try {
        zipFile = new ZipFile(srcFile);
        Enumeration<?> entries = zipFile.entries();
        while (entries.hasMoreElements()) {
            ZipEntry entry = (ZipEntry) entries.nextElement();
            System.out.println("解压" + entry.getName());
            // 如果是文件夹,就创建个文件夹
            if (entry.isDirectory()) {
                String dirPath = destDirPath + "/" + entry.getName();
                File dir = new File(dirPath);
                dir.mkdirs();
            } else {
                // 如果是文件,就先创建一个文件,然后用io流把内容copy过去
                File targetFile = new File(destDirPath + "/" + entry.getName());
                // 保证这个文件的父文件夹必须要存在
                if (!targetFile.getParentFile().exists()) {
                    targetFile.getParentFile().mkdirs();
                }
                targetFile.createNewFile();
                // 将压缩文件内容写入到这个文件中
                InputStream is = zipFile.getInputStream(entry);
                FileOutputStream fos = new FileOutputStream(targetFile);
                int len;
                byte[] buf = new byte[BUFFER_SIZE];
                while ((len = is.read(buf)) != -1) {
                    fos.write(buf, 0, len);
                }
                // 关流顺序,先打开的后关闭
                fos.close();
                is.close();
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("解压完成,耗时:" + (end - start) + " ms");
    } catch (Exception e) {
        throw new RuntimeException("unzip error from ZipUtils", e);
    } finally {
        if (zipFile != null) {
            try {
                zipFile.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

2.数组按指个数分割(自己写的,烂勿喷)

/**
 * 数组按个数分割
 *
 * @param list 源数组
 * @param size 数量
 * @return
 */
public static List<List<Integer>> splitList(List<Integer> list, int size) {
    List<List<Integer>> result = new ArrayList<>();
    int index = 0;
    for (int i = 0; i < list.size(); i = i + size) {
        List<Integer> temp = new ArrayList<>();
        for (int j = 0; j < size; j++) {
            temp.add(list.get(index++));
            if (index >= list.size())
                break;
        }
        result.add(temp);
    }
    return result;
}

3.文件夹复制,文件的复制

/**
 * 拷贝文件夹
 *
 * @param oldPath
 * @param newPath
 * @throws IOException
 */
public static void copyDir(String oldPath, String newPath) throws IOException {
    File file = new File(oldPath);
    //文件名称列表
    String[] fileNameList = file.list();

    if (!(new File(newPath)).exists()) {
        (new File(newPath)).mkdir();
    }

    for (String fileName : fileNameList) {
        if ((new File(oldPath + File.separator + fileName)).isDirectory()) {
            copyDir(oldPath + File.separator + fileName, newPath + File.separator + fileName);
        }

        if (new File(oldPath + File.separator + fileName).isFile()) {
            copyFile(oldPath + File.separator + fileName, newPath + File.separator + fileName);
        }

    }
}


/**
 * 文件复制
 *
 * @param srcPath     源文件路径
 * @param newFileName 复制后存放路径
 * @throws Exception
 */
public static void copyFile(String srcPath, String newFileName) {
    File srcFile = new File(srcPath);

    File targetFile = new File(newFileName);
    FileInputStream in = null;
    FileOutputStream out = null;
    try {
        in = new FileInputStream(srcFile);
        out = new FileOutputStream(targetFile);
        //从in中批量读取字节,放入到buf这个字节数组中,
        // 从第0个位置开始放,最多放buf.length个 返回的是读到的字节的个数
        byte[] buf = new byte[8 * 1024];
        int len = 0;
        while ((len = in.read(buf)) != -1) {
            out.write(buf, 0, len);
            out.flush();
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (in != null) {
                in.close();
            }
        } catch (Exception e) {
            System.out.println("关闭输入流错误!");
        }
        try {
            if (out != null) {
                out.close();
            }
        } catch (Exception e) {
            System.out.println("关闭输出流错误!");
        }
    }

}

4.删除文件或者文件夹以下所有文件

/**
 * 删除文件或者文件夹以下所有文件
 *
 * @param file
 */
public static void deleteAllFilesOfDir(File file) {
    if (!file.exists())
        return;
    if (file.isFile()) {
        boolean delete = file.delete();
        if (!delete) {
            System.gc();    //回收资源
            file.delete();
        }
        return;
    }
    File[] files = file.listFiles();
    for (int i = 0; i < files.length; i++) {
        deleteAllFilesOfDir(files[i]);
    }
    file.delete();
}

/**
 * 删除单个文件
 *
 * @param file
 * @return
 */
public static boolean delFile(File file) {
    if (!file.exists()) {
        return false;
    }
    if (file.isFile())
        return file.delete();
    else
        return false;
}

软件使用说明书


安装PC上的Java环境

教程百度

下载应用的app 下载地址

Github源码地址:https://github.com/yttrium2016/iconfontTools

求Github小伙伴们使用的人小星星 收藏

使用图解

下载解压工具

双击打开

进入设置

浏览器(建议使用Chrome浏览器)打开 网址https://www.iconfont.cn/ 登录确认登录状态下.

F12 打开控制台 点击任意一个请求

用鼠标复制cookie里面的所有字符

在软件的设置页面cookies列黏贴进去 点击保存

在工具页面输入https://www.iconfont.cn/里面任意一个图标库的URL上的cid编号

点击下载

稍等后下载完后就会在软件根目录有个文件名叫icon的文件夹里面就是下载的图标文件

软件使用教程结束

PS:每次下载时候会清楚上次下载的文件 所以下载完后请自己手动移出文件夹

最后说两句

本人很懒,所以很多事总是在拖延症中,放弃了,希望可以督促自己以后勤奋点,因为懒,所以去开发工具让自己干活变得容易,挺好的,有时候还是要折腾.学习…

文章作者: 杨振宇
文章链接: https://www.yangzhenyu.com.cn/33600/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 杨振宇 个人经验
支付宝打赏
微信打赏

Gitalk 加载中 ...