iconfont图标库 一键下载工具开发记录
Github源码地址:https://github.com/yttrium2016/iconfontTools
软件下载地址:发布地址
前言
最近自己想自己制作个App在收集图标 给自己开发使用。
于是找了很有名的[图标库]去找图标但是本来觉得很容易的一件事没想到困难重重,不过作为一个免费的信息提供者,阿里巴巴图标库已经做到了很好很好了.
遇到的几个问题
我是用于App的开发,我对svg图片的使用不是很明白,所以我选择简单的下载PNG格式的图片加入自己项目中,从中发现了几个问题
随便找的一个图标库,我想下载对应的所有图标
第一步,登录 (Github授权,微博授权,内部员工帐号)
程序员都有Github帐号的吧登录应该不成问题。
第二步,点击每个图片下载。(放弃)
真的是太麻烦了。几十个图标要点几十次才下载完成,耗时耗力。
第三步,批量下载 (可以考虑)
我发现可以通过把喜欢的图片加入购物车来批量下载。
本来觉得这个完美的解决了我的需求。随后发现了一个很坑爹的地方。
没错,当图标个数大于20的时候就会不让下载,也就是当一个图标库里面图标个数大于20的时候你要下载图标的时候就要
- 先用鼠标把前20个图标加入购物车
- 点击购物车去下载对应图标
- 在把购物车清空
- 在点接下来的20个图标加入购物车
- 在下载对应的图标
- ……
- 最后下载的一堆压缩包 解压复制出来。
太麻烦了,对我们来说只是想下载40-50个图标 却要在网页上反复操作很多很多次,作为一个创作型程序员来说,有需求,有问题,就让我们用程序来解决问题。
准备开发iconfont图标下载工具
根据上面的想法,我萌生了开发一个自己使用的小工具的想法.
功能设计:
- 首先,输入网址(编号)自动获取所有当个库中所有图标.
- 使用批量下载,根据获取的图标个数 进行(num/20)次下载 下载完所有图标
- 解压所有下载完成后的压缩包
- 把所有的解压完的图标复制到统一文件夹
- 清除多余的无用缓存文件
- 最好有可以设置的下载参数的配置
- 其他可以优化的逻辑..
本人使用的技术栈是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:每次下载时候会清楚上次下载的文件 所以下载完后请自己手动移出文件夹
最后说两句
本人很懒,所以很多事总是在拖延症中,放弃了,希望可以督促自己以后勤奋点,因为懒,所以去开发工具让自己干活变得容易,挺好的,有时候还是要折腾.学习…