iPic 是一个很赞的应用,可以快速将图片上传到图床上。由于非会员只能使用免费的新浪图床,因为最近新浪图床防盗链和图片有效期的缘故,因此决定自己实现一个图片快速上传的应用。
大致对比了一下Flutter Desktop、PyQT和Electron等框架,最后决定使用Electron,花了两三个晚上实现了将剪切板的图片快速上传到七牛上(非广告~)。
本文将回顾整个开发流程,并记录第一次正儿八经开发Electron的经验。

项目完整代码已放在github上。
准备工作
开发安环境
electron-forge是一个用来开发、打包和发布 Electron 的脚手架,首先安装electron和electron-forge

全局安装

npm install -g electron
npm install -g electron-forge

初始化项目

electron-forge init oPic

…初始化的时间可能会有点长

复制代码electron-forge 为我们生成了基本的项目模板。
如果是开发类似于 GUI 应用,可以修改src/index.html里面相关的视图文件,体验使用 Web 技术开发桌面应用。如果引入了 Vue、React 等框架,也可以在开发环境下直接将file://文件替换为webpack-dev-server服务 URL
// mainWindow.loadURL(file://${__dirname}/index.html);
mainWindow.loadURL(http://localhost:8080);
复制代码需求梳理
由于本次开发目标是工具类应用,很少涉及到 UI 层面的开发,梳理一下整个工具的需求

点击顶部任务栏应用图标,展示剪贴板内的图片(如复制图片文件、截图等),下面是 iPic 的工作页面

点击待上传图片,后台将图片上传到图床,自动将图片 URL 填充到剪贴板

需要提供图床配置
出于图床的容量和流量的考虑,希望在图片上传之前进行压缩

更新已上传图片列表,点击已上传图片,会重新复制该图片的 URL

整个需求比较简单,主要需要去Electron 文档查下面几个接口

在 Mac 顶部应用栏展示应用图标,点击弹出选项菜单
获取剪切板图片信息,定制选项菜单栏展示图片
图床配置弹窗 UI 开发,与主进程交互
图片上传,很早之前写了一个img_qiniu_cdn,貌似现在还可以用
图片压缩,本来想使用TinyPng的 API,发现有次数限制~找个其他的库吧

大概就这些,开始写代码啦
开发
顶部应用栏
查看系统托盘 API,构造一个Tray实例
import { Tray } from “electron”;
// 创建顶部图标
const createTray = app => {
// upload@3x是展示在托盘的图标
const icon16 = path.resolve(__dirname, “…/assets/upload@3x.png”);
const tray = new Tray(icon16);
// 监听图标点击事件,打开选项菜单
tray.on(“click”, () => {
const config = configUtil.getConfig();
const template = [
{ label: “待上传”, type: “normal”, enabled: false },
{ label: “”, type: “separator” },
{ label: “已上传”, type: “normal”, enabled: false },
{ label: “”, type: “separator” }
];
// todo 构建图片选项

    // 创建contextMenu并弹出const contextMenu = Menu.buildFromTemplate(template);tray.popUpContextMenu(contextMenu);
});

};
复制代码获取剪贴板图片
参考

剪贴板 API,使用clipboard.readImage获取剪贴板内的图片
NativeImage,readImage获取的是一个 NativeImage 包装对象,需要查看它与原始图片文件之间的转换

import { clipboard } from “electron”;

const uploadList = []; // 将已上传的图片保存在内存中
const clipboardImageList = []; // 保存最近未上传的图片

// 根据剪切板图片创建menuItem
const createClipboardImageItem = () => {
const clipboardImage = clipboard.readImage();
// 剪切板如果有数据,则保存到clipboardImageList中
if (clipboardImage && !clipboardImage.isEmpty()) {
const radio = clipboardImage.getAspectRatio();
// 创建一个用于在菜单栏展示的图标
const img = clipboardImage.resize({
width: 100,
height: radio / 100
});

    // 将图片暂存在clipboardImageList中addToImageList(clipboardImageList, { img, raw: clipboardImage }, 1);
}return clipboardImageList.map((row, index) => {const { raw, img } = row;// 点击菜单选项时执行uploadconst upload = () => {const buffer = raw.toPNG();// 调用uploadBufferImage方法上传图片uploadBufferImage(buffer).then(url => {// 更新列表addToImageList(uploadList, { img, url });removeFromClipboardList(img);// 自动复制urlcopyUrl(url);Util.showNotify(`上传到七牛成功,链接${url}已经复制到剪切板`);});};// 返回菜单栏配置return {label: (index + 1).toString(),icon: row.img, // 缩小版的图片传给icon配置项,这样就可在菜单栏展示了type: "normal",click: upload};
});

};

// 同理,创建已上传的图片记录
const createUploadItem = () =>
uploadList.map(({ img, url }, index) => {
const handler = () => {
const text = copyUrl(url);
Util.showNotify(链接${text}已经复制到剪切板);
};
return {
label: (index + 1).toString(),
icon: img,
type: “normal”,
click: handler
};
});
复制代码七牛配置
希望应用足够轻量,因此在数据存储方便并没有使用诸如nedb等工具,而是直接简单粗暴地保存在本地文件。
当点击菜单栏的配置项时,将弹出一个配置窗口填写配置项,确定时将数据保存在本地文件中。
let settingWindow;
const openSettingWindow = () => {
settingWindow = new BrowserWindow({
width: 600,
height: 400
});
// 使用electron渲染一个页面
const url = file://${path.resolve(__dirname, "./setting.html")};
settingWindow.loadURL(url);
// settingWindow.webContents.openDevTools();
settingWindow.on(“closed”, () => {
settingWindow = null;
});
};

const template = [
// …增加一个菜单选项
{ label: “偏好设置”, type: “normal”, click: openSettingWindow }
];
复制代码在setting.html中,实现一个表单提交的页面,

然后通过封装的本地存储工具configUtil读取和保存配置
const defaultConfig = {
autoMarkdown: true,
upload: {
// 七牛图床配置
qiNiu: {
accessKey: “”,
secretKey: “”,
bucket: “”, // 仓库名
host: “” // 资源域名
}
}
};
const configFile = “…/config.json”;
// 获取配置
function getConfig() {
try {
return require(configFile);
} catch (e) {
return defaultConfig;
}
}
// 保存配置
function saveConfig(config) {
const fileName = path.resolve(__dirname, configFile);
return fs.writeFile(fileName, JSON.stringify(config));
}
复制代码除了图床配置,configUtil还可以可以保存应用偏好设置,在设计上也需要支持后续其他图床的扩展(虽然我目前用七牛就够啦~)
图片上传
回到前面的uploadBufferImage方法,由于没有找到直接上传 Electron NativeImage 的方法,因此这里的实现思路是:
首先读取七牛配置,然后将 buffer 写入本地临时文件,接着通过 qiniu SDK 将文件上传到服务器上,最后删除临时文件就 OK 了
function qiNiuUpload(img) {
try {
const { upload: uploadConfig } = configUtil.getConfig();
const upload = createUploadQiNiu(uploadConfig.qiNiu);

    return upload(img);
} catch (e) {console.log("缺少config.json配置文件");return Promise.reject(e);
}

}

// 上传二进制文件
async function uploadBufferImage(buffer) {
// 写入临时图片
const fileName = ${Date.now()}_${Math.floor(Math.random() * 1000)};
const filePath = path.resolve(__dirname, ../tmp/${fileName}.png);

await fs.writeFile(filePath, buffer); // 创建临时本地文件
const url = await qiNiuUpload(filePath); // 上传到七牛
await fs.unlinkSync(filePath); // 删除临时文件
return url;

}
复制代码下面这个createUploadQiNiu是封装qiniuSDK 的方法,三年前的代码了[/捂脸],凑活着用
const qiniu = require(“qiniu”);
const path = require(“path”);
const createUploadQiNiu = opts => {
const { accessKey, secretKey, bucket, host } = opts;

return filePath => {const key = `oPic/${path.basename(filePath)}`;// 设置上传策略const putPolicy = new qiniu.rs.PutPolicy({scope: `${bucket}:${key}`});// 根据密钥创建鉴权对象mac,获取上传tokenconst mac = new qiniu.auth.digest.Mac(accessKey, secretKey);const uploadToken = putPolicy.uploadToken(mac);// 配置对象const config = new qiniu.conf.Config();// 上传机房,z2是华南config.zone = qiniu.zone.Zone_z2;// 扩展参数,主要是用于文件分片上传使用的,这里可以忽略const putExtra = new qiniu.form_up.PutExtra();// 实例化上传对象const formUploader = new qiniu.form_up.FormUploader(config);return new Promise((resolve, reject) => {formUploader.putFile(uploadToken,key,filePath,putExtra,(respErr, respBody, respInfo) => {if (respErr) {reject(respErr);}if (respInfo && respInfo.statusCode === 200) {// 拼接服务器路径const filename = host + key;resolve(filename);} else {reject("respInfo is error");}});});
};

};
复制代码图片压缩
前面提到,希望在图片上传之前对文件进行压缩,目前用过最好的图片压缩还是TinyPNG,不过貌似没开源压缩算法,目前一个月只能调用500次API,所以试了下imagemin,看起来效果也能接受,就它啦。
const imageMin = require(“imagemin”);
const imageJPEG = require(“imagemin-jpegtran”);
const imagePNG = require(“imagemin-pngquant”);

// 图片压缩
function compressImage(filePath, destination) {
return imageMin([filePath], {
destination,
plugins: [
imageJPEG(),
imagePNG({
quality: [0.6, 0.8]
})
]
});
}
复制代码然后再上传前进行压缩即可
await fs.writeFile(filePath, buffer); // 创建临时本地文件
// 图片压缩为同名图片
if (needCompress) {
await compressImage(filePath, folder);
}
const url = await qiNiuUpload(filePath); // 上传到七牛
复制代码打包
功能开发完毕后,使用electron-forge打包就可以啦,会在项目根目录下输出out文件夹
npm run package
npm run make
复制代码此外,Electron 打包的应用是在是太大了,上面这点代码打包出来居然有 150M,不知道是不是我的姿势不对,有空研究下

https://www.jianshu.com/p/afb04c02bdc2
https://www.jianshu.com/p/9a4d54616cda
https://www.jianshu.com/p/afe0b866b620
https://www.jianshu.com/p/b9a0efcd4cd5
https://www.jianshu.com/p/b94079b1eda9
https://www.jianshu.com/p/0df3f258e778
https://www.jianshu.com/p/58307ddd4bfd
小结
至此,就完成了一个简易版的图片上传应用,目前基本能实现日常的需求了(PS:现在终于不用担心博客的图片被新浪图床吞掉了~),也算是完成了自己的一个挂念。
整个项目已放在github上,由于开发时间有点短,加上之前也基本没用过 Electron,所以代码写的有点烂~ 还有一些可以迭代的地方,比如

上传进度展示

使用Electron实现一个iPic相关推荐

  1. 用electron做一个浏览器

    文章目录 iframe 输入网址 源码地址: electron做一个丐版浏览器,只有输入网址显示网页的功能,适合学习 iframe electron的窗口控件,实际上就是一个html解析工具,也就是说 ...

  2. js桌面应用 Linux,从 1 到完美,用 js 和 electron 写一个桌面应用

    从 1 到完美,用 js 和 electron 写一个桌面应用 目前用 js 和前端技术写桌面软件的方案主要有两种:electron 和 nw.js.这两者在底层实现上有所不同,简单的说,electr ...

  3. 2021了,你还不会用 Electron 写一个桌面应用?

    正如 Electron 官方所说"如果你可以建一个网站,你就可以建一个桌面应用程序". ‍ 作为一个跨平台的桌面应用开发框架,Electron 的迷人之处在于,它是建立在 Chro ...

  4. 使用Electron制作一个快速搜索应用(入门向)

    什么是Electron Electron是由Github开发,用HTML,CSS和JavaScript来构建跨平台桌面应用程序的一个开源库. Electron通过将Chromium和Node.js合并 ...

  5. 跟我一起使用electron搭建一个文件浏览器吧

    这个文件浏览器应用可以具备以下两种功能: 一:用户浏览器文件夹和查找文件 二:用户可以使用默认的应用程序打开文件 接下来我们开始进行开发吧 第一步创建文件和文件夹: mkdir lorikeet-el ...

  6. Electron实现一个简易的编辑器

    众所周知,大名鼎鼎的VS Code编辑器是用Electron写出来的,近日因为工作需要,对Electron进行了复习和实践,写了一个简单的编辑器. 编器主要实现了这些功能:应用菜单.右键菜单.新建.打 ...

  7. (win环境)使用Electron打造一个桌面应用翻译小工具

    初始化项目 npm init 修改package.json {"name": "trans","version": "1.0.0& ...

  8. electron 安装import_Electron: 从零开始写一个记事本app

    Electron介绍 简单来说,Electron就是可以让你用Javascript.HTML.CSS来编写运行于Windows.macOS.Linux系统之上的桌面应用的库.本文的目的是通过使用Ele ...

  9. electron 两个窗口如何通信_关于 Electron 进程间通信的一个小小实践

    Electron 是一个跨平台桌面框架,它集成了 node.js 和 chromium,所以我们可以借助 node.js 实现桌面客户端访问操作系统资源的功能(出于安全,浏览器是不可以访问操作系统的) ...

最新文章

  1. 36 岁开发者应聘被拒,这 3 位 50 岁程序员的生存秘籍送给你!
  2. 作为一个前端,可以如何机智地弄坏一台电脑?
  3. ctf 改变图片高度_每天一分钟,python一点通(opencv的图片处理方法)
  4. springboot定时发送短信_阿里大于可以发送定时短信
  5. log添加 oracle redo_Redo Log之一:理解Oracle redo log
  6. 关于 Matlab R2014a下载与安装流程
  7. PASS云计算书简介——接近完美的模式
  8. vue项目打包部署到tomcat服务器
  9. 分治法——最大子列和问题
  10. Guava Joiner
  11. android连接wifi不能上网,手机已经连接wifi但无法上网的详细解决方法
  12. 用 Swift、Foursquare API 和 Realm 創建一個咖啡屋 App
  13. PyQt5 作图之 pyqtgraph PlotWidget 代码结构拆解
  14. 二十四孝{做人不孝无异于禽兽,请宏扬我中华美德!}组图
  15. TI培训——电子电路基础知识讲座(第三章上)
  16. python公司网站毕业设计开题报告
  17. 六级考研单词之路-四十八
  18. linux调整主被动模式,Ftp修改为主被动模式命令
  19. oracle++卸载grid,GRID卸载及重新安装
  20. Debian Linux的简单网络设置

热门文章

  1. APC 英飞(InfraStruXure)解决方案助力宁波大红鹰“腾飞”
  2. npm更新命令更新最新版本
  3. F28335基础例程(全) CCS5.5和CCS6.0,基于手把手教学视频
  4. 解决Tomcat报错:The specified Tomcat installation does not exist.
  5. ECharts---一个非常炫酷好用的图表库。发现自己写的好详细。怪不得当时弄那么久。
  6. 批量修改文件名,我3分钟就搞定了
  7. 10kv电压互感器型号_10KV上互感器的选型
  8. 3.ABP连接多个数据库(可以是不同的数据库)
  9. android通过Intent调用手机图片,音频,视频录音拍照等代码
  10. Linux Jenkins查找缓存文件及删除 (2022-07测试可用)