├── .DS_Store ├── .gitignore ├── README.md ├── _config.yml ├── assets ├── icon.png ├── tinypng.icns ├── tinypng.ico └── tinypng.png ├── main.js ├── main ├── engine │ ├── README.md │ ├── files.js │ ├── index.js │ ├── package.json │ └── utli.js ├── task.js └── update.js ├── package-lock.json ├── package.json ├── renderer ├── index.html ├── jquery-1.9.1.min.js ├── main.css ├── preload.js └── render.js └── yarn.lock /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/focusbe/tinyImage/d08541dc0d17182952f95ef9af89e801a8ee1fee/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | output/ 3 | yarn* 4 | .vscode/ 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TinyImage 2 | 3 | 当我们需要上传大量的图片时,大小过大的图片会大大降低网页或应用的加载速度,给用户带来不良体验。因此,我们需要对图片进行压缩,以提高图片加载速度和页面性能。这时,我们可以使用一个名为 Tinyimage 的图片压缩软件,它可以帮助我们轻松地压缩图片,而且操作简单方便。 4 | 5 | Tinyimage 是一个开源的图片压缩工具,支持 JPG、PNG、GIF 等多种图片格式,它可以将图片文件大小缩小到原来的 70% 以下,而且不会损失图片的视觉质量。在压缩过程中,Tinyimage 会自动优化压缩算法,让用户能够得到最优的压缩结果。 6 | 7 | 使用 Tinyimage 压缩图片非常简单,只需要将需要压缩的图片拖放到 Tinyimage 窗口中,即可自动压缩图片并显示压缩后的文件大小和压缩比例。另外,用户还可以通过调整 Tinyimage 的压缩设置,来获得更好的压缩效果。 8 | 9 | Tinyimage 的界面简洁明了,提供了良好的用户体验,而且功能强大,使用方便。它还提供了命令行接口,用户可以在命令行中使用 Tinyimage 进行图片压缩,极大地方便了开发者的使用。 10 | 11 | 总之,Tinyimage 是一个非常优秀的图片压缩工具,它不仅提供了强大的压缩功能,而且使用起来十分简单。对于需要频繁上传图片的用户和开发者来说,Tinyimage 是一个必备的工具。 12 | 13 | ## 下载 14 | ### [下载](https://github.com/focusbe/tinyImage/releases) 15 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/focusbe/tinyImage/d08541dc0d17182952f95ef9af89e801a8ee1fee/assets/icon.png -------------------------------------------------------------------------------- /assets/tinypng.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/focusbe/tinyImage/d08541dc0d17182952f95ef9af89e801a8ee1fee/assets/tinypng.icns -------------------------------------------------------------------------------- /assets/tinypng.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/focusbe/tinyImage/d08541dc0d17182952f95ef9af89e801a8ee1fee/assets/tinypng.ico -------------------------------------------------------------------------------- /assets/tinypng.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/focusbe/tinyImage/d08541dc0d17182952f95ef9af89e801a8ee1fee/assets/tinypng.png -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | const { app, BrowserWindow, Menu } = require('electron'); 2 | const Update = require('./main/update'); 3 | 4 | const isMac = process.platform === 'darwin'; 5 | const path = require('path'); 6 | global.Win = null; 7 | function createWindow() { 8 | // 创建浏览器窗口 9 | let win = new BrowserWindow({ 10 | width: 400, 11 | height: 300, 12 | acceptFirstMouse: true, 13 | webPreferences: { 14 | nodeIntegration: true, 15 | preload: path.resolve(__dirname, './renderer/preload.js'), 16 | }, 17 | show: false, 18 | backgroundColor: '#24292e', 19 | }); 20 | win.once('ready-to-show', function () { 21 | win.show(); 22 | Update.check(); 23 | }); 24 | // 加载index.html文件 25 | win.loadFile('renderer/index.html'); 26 | global.Win = win; 27 | } 28 | 29 | app.whenReady().then(createWindow); 30 | 31 | template = [ 32 | ...(isMac 33 | ? [ 34 | { 35 | label: app.name, 36 | submenu: [ 37 | { role: 'about', label: '关于' }, 38 | { type: 'separator' }, 39 | { type: 'separator' }, 40 | { role: 'hide', label: '隐藏' }, 41 | { role: 'hideOthers', label: '隐藏其他' }, 42 | { role: 'unhide', label: '显示' }, 43 | { type: 'separator' }, 44 | { role: 'quit', label: '退出' }, 45 | ], 46 | }, 47 | ] 48 | : []), 49 | // { role: 'fileMenu' } 50 | // { role: 'editMenu' } 51 | // { 52 | // label: 'Edit', 53 | // submenu: [ 54 | // { role: 'undo' }, 55 | // { role: 'redo' }, 56 | // { type: 'separator' }, 57 | // { role: 'cut' }, 58 | // { role: 'copy' }, 59 | // { role: 'paste' }, 60 | // ...(isMac 61 | // ? [ 62 | // { role: 'pasteAndMatchStyle' }, 63 | // { role: 'delete' }, 64 | // { role: 'selectAll' }, 65 | // { type: 'separator' }, 66 | // { 67 | // label: 'Speech', 68 | // submenu: [ 69 | // { role: 'startSpeaking' }, 70 | // { role: 'stopSpeaking' }, 71 | // ], 72 | // }, 73 | // ] 74 | // : [ 75 | // { role: 'delete' }, 76 | // { type: 'separator' }, 77 | // { role: 'selectAll' }, 78 | // ]), 79 | // ], 80 | // }, 81 | // { role: 'viewMenu' } 82 | 83 | // { role: 'windowMenu' } 84 | { 85 | label: '窗口', 86 | submenu: [ 87 | { role: 'minimize' , label: '最小化'}, 88 | { role: 'zoom', label: '缩放' }, 89 | ...(isMac 90 | ? [ 91 | { type: 'separator' }, 92 | { role: 'front' , label: '前置'}, 93 | ] 94 | : [{ role: 'close' , label: '关于'}]), 95 | ], 96 | }, 97 | { 98 | label: '帮助', 99 | role: 'help', 100 | submenu: [ 101 | { 102 | label: '了解更多', 103 | click: async () => { 104 | const { shell } = require('electron'); 105 | await shell.openExternal('https://focusbe.github.io/tinyImage/'); 106 | }, 107 | }, 108 | ], 109 | }, 110 | ]; 111 | 112 | const menu = Menu.buildFromTemplate(template); 113 | Menu.setApplicationMenu(menu); 114 | -------------------------------------------------------------------------------- /main/engine/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: 图片压缩工具 3 | --- 4 | 5 | # tinypngjs 6 | 7 | ## 安装 8 | 9 | ```bash 10 | npm install tinypngjs 11 | ``` 12 | 13 | ```bash 14 | var TinyPng = require("tinypngjs"); 15 | ``` 16 | 17 | ## API 18 | 19 | #### TinyPng.compress\(fromFolder,\[outFolder,onProgress\]\); 20 | 21 | 22 | 参数: 23 | 24 | * fromFolder:需要压缩的文件夹 25 | * outFolder:压缩后图片保存的文件夹 26 | * 可选 27 | * 默认值=fromFolder 28 | * onProgress:下载进度回调 29 | 30 | 回调函数:function\(res,percent\){} 31 | 32 | * res: Object,tinyjs压缩图片后返回的json 33 | * percent:Number当前进度 34 | 35 | 返回值 Promise 36 | 37 | ```javascript 38 | var res = await TinyPng.compress("./a/"); 39 | ``` 40 | 41 | 42 | 43 | #### TinyPng.compressImage\(fromImg,\[outImg\]\); 44 | 45 | 46 | 参数: 47 | 48 | * fromImg:需要压缩的图片路径 49 | * outFolder:压缩后图片的图片路径 50 | * 可选 51 | * 默认值=fromImg 52 | 53 | 返回值 Promise 54 | 55 | ```javascript 56 | var res = await TinyPng.compressImg("./a/1.jpg"); 57 | ``` 58 | 59 | 60 | 61 | ### Github 62 | 63 | nodejs版:[https://github.com/focusbe/tinypngjs](https://github.com/focusbe/tinypngjs) 64 | 客户端版:[https://github.com/focusbe/tinyImage](https://github.com/focusbe/tinyImage) 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /main/engine/files.js: -------------------------------------------------------------------------------- 1 | const fse = require("fs-extra"); 2 | const path = require("path"); 3 | class Files { 4 | static copy(src, dst, callback) { 5 | var _self = this; 6 | fse.copy(src, dst, err => { 7 | callback(err); 8 | }); 9 | } 10 | static async getAlivpath(filepath, number) { 11 | if (!number) { 12 | number = 0; 13 | } 14 | let exists = await fse.pathExists(filepath); 15 | if (exists) { 16 | let extname = path.extname(filepath); 17 | let basename = path.basename(filepath, extname); 18 | let dirname = path.dirname(filepath); 19 | number++; 20 | basename = basename + "" + number; 21 | filepath = path.join(dirname, basename + extname); 22 | return await this.getAlivpath(filepath, number); 23 | } else { 24 | return filepath; 25 | } 26 | } 27 | static async createdirAsync(src) { 28 | let exists = await fse.exists(src); 29 | if (exists) { 30 | return true; 31 | } 32 | var res = await new Promise((result, reject) => { 33 | this.createdir(src, success => { 34 | if (!!success) { 35 | result(success); 36 | } else { 37 | reject("创建文件失败"); 38 | } 39 | }); 40 | }); 41 | return res; 42 | } 43 | static async getMtime(file) { 44 | let exists = await fse.exists(file); 45 | if (exists) { 46 | try { 47 | var stats = await fse.stat(file); 48 | } catch (error) { 49 | return false; 50 | } 51 | if (!!stats && !!stats.mtime) { 52 | 53 | return stats.mtime.getTime(); 54 | } 55 | return false; 56 | } 57 | return false; 58 | } 59 | static async writeFile(src, content) { 60 | var res = await new Promise((resolve, reject) => { 61 | this.createdir(path.dirname(src), function(bool) { 62 | if (!bool) { 63 | reject("创建目录失败"); 64 | return; 65 | } 66 | fse.writeFile(src, content, function(err) { 67 | if (err) reject(err); 68 | else { 69 | resolve(true); 70 | } 71 | }); 72 | }); 73 | }); 74 | return res; 75 | } 76 | static async writeJson(src, json) { 77 | var res = await new Promise((resolve, reject) => { 78 | this.createdir(path.dirname(src), function(bool) { 79 | if (!bool) { 80 | reject("创建目录失败"); 81 | return; 82 | } 83 | fse.writeJson(src, json, { spaces: 4, EOL: "\n" }, function( 84 | err 85 | ) { 86 | if (err) reject(err); 87 | else { 88 | resolve(true); 89 | } 90 | }); 91 | }); 92 | }); 93 | return res; 94 | } 95 | static createdir(src, callback) { 96 | let parentdir = path.dirname(src); 97 | fse.exists(parentdir, function(exists) { 98 | if (!exists) { 99 | Files.createdir(parentdir, function() { 100 | try { 101 | fse.mkdir(src, function() { 102 | //创建目录 103 | callback(src); 104 | }); 105 | } catch (error) { 106 | callback(false, error); 107 | } 108 | }); 109 | } else { 110 | 111 | fse.exists(src, function(exists) { 112 | if (exists) { 113 | //存在 114 | callback(src); 115 | } else { 116 | //bu存在 117 | try { 118 | fse.mkdir(src, function() { 119 | //创建目录 120 | callback(src); 121 | }); 122 | } catch (error) { 123 | callback(false, error); 124 | } 125 | } 126 | }); 127 | } 128 | }); 129 | } 130 | static getTree(src, istree, folder,testfun) { 131 | if (!folder) { 132 | folder = []; 133 | } 134 | if (typeof istree == "undefined") { 135 | istree = true; 136 | } 137 | return new Promise(function(result, reject) { 138 | fse.pathExists(src, function(err, exists) { 139 | if (!!err) { 140 | reject(err); 141 | return; 142 | } 143 | if (!exists) { 144 | result(folder); 145 | } 146 | fse.readdir(src, function(err, paths) { 147 | if (!err) { 148 | var promisArr = []; 149 | var length = paths.length; 150 | var done = 0; 151 | if(length==0){ 152 | result(folder); 153 | } 154 | paths.forEach(function(curpath) { 155 | 156 | var _src = path.resolve(src,curpath); 157 | var filestat = fse.statSync(_src); 158 | if (filestat) { 159 | if (filestat.isDirectory()) { 160 | var saveArr = folder; 161 | if (istree) { 162 | var _folder = { 163 | name: curpath, 164 | children: [] 165 | }; 166 | folder.push(_folder); 167 | saveArr = _folder["children"]; 168 | } 169 | 170 | Files.getTree(_src, istree, saveArr,testfun) 171 | .then(function(res) { 172 | done++; 173 | if (done >= length) { 174 | result(folder); 175 | } 176 | }) 177 | .catch(function() { 178 | done++; 179 | if (done >= length) { 180 | result(folder); 181 | } 182 | }); 183 | } else { 184 | if(!!testfun&&testfun(curpath)){ 185 | done++; 186 | return true; 187 | } 188 | done++; 189 | folder.push({ 190 | path: _src, 191 | name: path.basename(_src) 192 | }); 193 | if (done >= length) { 194 | result(folder); 195 | } 196 | } 197 | } else { 198 | done++; 199 | reject("获取文件状态失败"); 200 | } 201 | }); 202 | } else { 203 | reject(err); 204 | } 205 | }); 206 | }); 207 | }); 208 | } 209 | static getList(src, hasFile, callback) { 210 | if (typeof hasFile == "function") { 211 | callback = hasFile; 212 | hasFile = []; 213 | } 214 | function run(callback){ 215 | fse.readdir(src, function(err, paths) { 216 | if (!!err || !paths) { 217 | callback(false); 218 | return; 219 | } 220 | var filestlist = new Object(); 221 | paths.forEach(function(curpath) { 222 | var _src = path.resolve(src, curpath); 223 | var readable; 224 | var writable; 225 | var filestat = fse.statSync(_src); 226 | var issure = true; 227 | if (filestat && filestat.isDirectory()) { 228 | var projectfiles = fse.readdirSync(_src); 229 | for (var filename in hasFile) { 230 | if (projectfiles.indexOf(filename) < 0) { 231 | issure = false; 232 | break; 233 | } 234 | } 235 | if (issure) { 236 | filestlist[curpath] = _src; 237 | } 238 | } 239 | }); 240 | callback(filestlist); 241 | }); 242 | } 243 | 244 | if(!callback){ 245 | callback = function(){}; 246 | return new Promise((resolve,reject)=>{ 247 | run(function(bool){ 248 | if(!bool){ 249 | reject('获取失败'); 250 | } 251 | resolve(bool); 252 | }) 253 | }); 254 | } 255 | 256 | } 257 | static exists(src, dst, callback) { 258 | fse.exists(dst, function(exists) { 259 | if (exists) { 260 | //不存在 261 | callback(src, dst); 262 | } else { 263 | //存在 264 | fse.mkdir(dst, function() { 265 | //创建目录 266 | callback(src, dst); 267 | }); 268 | } 269 | }); 270 | } 271 | static isdir(src, callback) { 272 | //判断打开的是文件 还是 文件夹 273 | fse.stat(src, function(err, stat) { 274 | if (err) { 275 | console.error(err); 276 | throw err; 277 | } 278 | callback(stat.isDirectory()); 279 | }); 280 | } 281 | static async delFile(filepath, times) { 282 | if (!times) { 283 | times = 0; 284 | } 285 | let exists = await fse.pathExists(filepath); 286 | if (exists) { 287 | var res = await new Promise(function(resolve, reject) { 288 | fse.unlink(filepath, function(err) { 289 | if (err) { 290 | if (times < 5) { 291 | setTimeout(async () => { 292 | times++; 293 | var res = await Files.delFile(filepath, times); 294 | resolve(false); 295 | }, 300); 296 | } else { 297 | resolve(false); 298 | } 299 | } else { 300 | resolve(true); 301 | } 302 | }); 303 | }); 304 | return res; 305 | } else { 306 | return true; 307 | } 308 | } 309 | static openFolder(folder) { 310 | var shell = require("electron").shell; 311 | //const os = require("os"); 312 | shell.showItemInFolder(folder); 313 | } 314 | } 315 | //export default Files; 316 | module.exports = Files; 317 | -------------------------------------------------------------------------------- /main/engine/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const { app } = require('electron'); 3 | const path = require('path'); 4 | const https = require('https'); 5 | const { URL } = require('url'); 6 | const fse = require('fs-extra'); 7 | const Files = require('./files'); 8 | const exts = ['.jpg', '.png', '.gif', '.webp', '.jpeg', '.svg']; //图片的格式,不一定压缩但是可以完成复制; 9 | const tinyExts = ['.jpg', '.png']; 10 | const max = 5200000; // 5MB == 5242848.754299136 11 | const imagemin = require('imagemin'); 12 | const imageminMozjpeg = require('imagemin-mozjpeg'); 13 | // const imageminPngquant = require('imagemin-pngquant'); 14 | const Utli = require('./utli'); 15 | const iconv = require('iconv-lite'); 16 | const _plugins = { 17 | jpg: imageminMozjpeg({ 18 | quality: 80, 19 | }), 20 | // png: imageminPngquant({ 21 | // quality: [0.6, 0.8], 22 | // }), 23 | }; 24 | class TinyPng { 25 | constructor() {} 26 | static compressList(imagelist, onprogress) { 27 | if (!imagelist || imagelist.length == 0) { 28 | throw new Error('没有获取到图片文件'); 29 | } 30 | var total = imagelist.length; 31 | var compressed = 0; 32 | for (var i in imagelist) { 33 | let curpath = imagelist[i].path; 34 | TinyPng.compressImg(curpath, curpath) 35 | .then((res) => { 36 | compressed++; 37 | if (!!onprogress) { 38 | onprogress(res, compressed / total); 39 | } 40 | }) 41 | .catch((err) => { 42 | compressed++; 43 | onprogress(false, compressed / total, err); 44 | }); 45 | } 46 | return true; 47 | } 48 | static async compress(from, out, onprogress) { 49 | if (!from) { 50 | throw new Error('请传入要压缩的文件夹'); 51 | } 52 | if (!out) { 53 | out = from; 54 | } 55 | var imagelist = await this.getAllImg(from); 56 | if (!imagelist || imagelist.length == 0) { 57 | throw new Error('没有获取到图片文件'); 58 | } 59 | var total = imagelist.length; 60 | var compressed = 0; 61 | for (var i in imagelist) { 62 | let curpath = imagelist[i].path; 63 | let relative = path.relative(from, curpath); 64 | let outputPath = path.resolve(out, relative); 65 | TinyPng.compressImg(curpath, outputPath) 66 | .then((res) => { 67 | compressed++; 68 | if (!!onprogress) { 69 | onprogress(res, compressed / total); 70 | } 71 | }) 72 | .catch((err) => { 73 | compressed++; 74 | onprogress(false, compressed / total, err); 75 | }); 76 | } 77 | return true; 78 | } 79 | static getPlugins(extname) { 80 | var plugins = []; 81 | if (extname == '.jpg' || extname == '.jpeg') { 82 | plugins.push(_plugins['jpg']); 83 | } 84 | // else if (extname == '.png') { 85 | // plugins.push(_plugins['png']); 86 | // } 87 | return plugins; 88 | } 89 | static uploadImage(imageData) { 90 | //上传到tinypng进行压缩 91 | if (!imageData || imageData.length > max) { 92 | return false; 93 | } 94 | return new Promise((resolve, reject) => { 95 | var req = https.request(Utli.getOptions(), (res) => { 96 | res.on('data', (buf) => { 97 | let obj; 98 | try { 99 | obj = JSON.parse(buf.toString()); 100 | } catch (error) { 101 | reject(new Error('解析返回值失败')); 102 | } 103 | if (obj.error) { 104 | reject(new Error(obj.error)); 105 | } else { 106 | resolve(obj); 107 | } 108 | }); 109 | }); 110 | req.write(imageData, 'binary'); 111 | req.on('error', (err) => { 112 | reject(err); 113 | }); 114 | req.end(); 115 | }); 116 | } 117 | 118 | static async compressImg(from, out, disableTiny) { 119 | if (!from) { 120 | throw new Error('请传入正确的from'); 121 | } 122 | if (!out) { 123 | out = from; 124 | } 125 | var exists = await fse.exists(from); 126 | if (!exists) { 127 | throw new Error('传入的文件不存在'); 128 | } 129 | var imageData; 130 | var stat; 131 | var res; 132 | stat = await fse.stat(from); 133 | console.log(stat); 134 | from = from.replace(/\\/g, '/'); 135 | var extname = path.extname(from).toLowerCase(); 136 | try { 137 | var image = await imagemin([from], { 138 | plugins: this.getPlugins(extname), 139 | }); 140 | imageData = image[0]['data']; 141 | } catch (error) { 142 | if (tinyExts.includes(extname)) { 143 | console.log(error); 144 | imageData = await fs.readFile(from); 145 | console.log(imageData); 146 | } else { 147 | throw error; 148 | return false; 149 | } 150 | } 151 | 152 | var resObj = { 153 | input: { size: stat.size, path: from }, 154 | type: 'imagemin', 155 | }; 156 | if (!disableTiny && tinyExts.indexOf(extname) > -1) { 157 | try { 158 | var obj = await this.uploadImage(imageData); 159 | if (obj && obj.output) { 160 | var content = await this.downloadFile(obj.output.url); 161 | if (content) { 162 | resObj.type = 'tinypng'; 163 | imageData = content; 164 | } 165 | } 166 | } catch (error) {} 167 | } 168 | var res = await this.saveImg(out, imageData); 169 | if (res) { 170 | resObj.output = { 171 | size: imageData.length, 172 | path: out, 173 | }; 174 | return resObj; 175 | } else { 176 | return false; 177 | } 178 | } 179 | static downloadFile(url) { 180 | //下载文件 181 | return new Promise((resolve, reject) => { 182 | let options = new URL(url); 183 | let req = https.request(options, (res) => { 184 | let body = ''; 185 | res.setEncoding('binary'); 186 | res.on('data', function (data) { 187 | body += data; 188 | }); 189 | res.on('end', function () { 190 | resolve(body); 191 | }); 192 | }); 193 | req.on('error', (e) => { 194 | reject(e); 195 | }); 196 | req.end(); 197 | }); 198 | } 199 | static saveImg(imgpath, content) { 200 | //保存在线压缩好的图片 201 | return new Promise((resolve, reject) => { 202 | Files.createdirAsync(path.dirname(imgpath)).then((res) => { 203 | fs.writeFile(imgpath, content, 'binary', (err) => { 204 | if (err) { 205 | reject(err); 206 | return; 207 | } 208 | resolve(true); 209 | }); 210 | }); 211 | }); 212 | } 213 | static async getAllImg(file) { 214 | var imgs = await Files.getTree(file, false, null, function (file) { 215 | var extname = path.extname(file).toLowerCase(); 216 | return !extname || !exts.includes(extname); 217 | }); 218 | return imgs; 219 | } 220 | } 221 | module.exports = TinyPng; 222 | -------------------------------------------------------------------------------- /main/engine/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tinypngjs", 3 | "version": "1.2.4", 4 | "description": "nodejs 调用tinypng 接口压缩图片", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/focusbe/tinypngjs.git" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "bugs": { 16 | "url": "https://github.com/focusbe/tinypngjs/issues" 17 | }, 18 | "homepage": "https://github.com/focusbe/tinypngjs#readme", 19 | "dependencies": { 20 | "fs-extra": "^8.1.0", 21 | "iconv-lite": "^0.5.1", 22 | "imagemin": "^7.0.1", 23 | "imagemin-mozjpeg": "^8.0.0", 24 | "imagemin-pngquant": "^8.0.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /main/engine/utli.js: -------------------------------------------------------------------------------- 1 | class Utli { 2 | static getOptions() { 3 | //生成请求头部 4 | var time = Date.now(); 5 | let UserAgent = "Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/" + 59 + Math.round(Math.random() * 10) + ".0.3497." + Math.round(Math.random() * 100) + "Safari/537.36"; 6 | var options = { 7 | method: "POST", 8 | hostname: "tinypng.com", 9 | path: "/backend/opt/shrink", 10 | headers: { 11 | rejectUnauthorized: false, 12 | "Postman-Token": (time -= 5000), 13 | "Cache-Control": "no-cache", 14 | "Content-Type": "application/x-www-form-urlencoded", 15 | "User-Agent": UserAgent, 16 | "X-Forwarded-For": Utli.getIp(), 17 | Cookie: "", 18 | }, 19 | timeout:5000 20 | }; 21 | return options; 22 | } 23 | static getIp() { 24 | var _ = { 25 | random: function (start, end) { 26 | return parseInt(Math.random() * (end - start) + start); 27 | }, 28 | }; 29 | var ip = _.random(1, 254) + "." + _.random(1, 254) + "." + _.random(1, 254) + "." + _.random(1, 254); 30 | return ip; 31 | } 32 | } 33 | module.exports = Utli; 34 | -------------------------------------------------------------------------------- /main/task.js: -------------------------------------------------------------------------------- 1 | // const path = require('path'); 2 | // const fs = require('fs'); 3 | // const { exec } = require('child_process'); 4 | const TinyPng = require('tinypngjs'); 5 | // const imagemin = require('imagemin'); 6 | // const imageminMozjpeg = require('imagemin-mozjpeg'); 7 | // const imageminPngquant = require('imagemin-pngquant'); 8 | // const Utli = require('./engine/utli'); 9 | // const iconv = require('iconv-lite'); 10 | // const remote = require('electron').remote; 11 | const compress = function (imagelist, onprogress) { 12 | TinyPng.compressList(imagelist, onprogress); 13 | }; 14 | module.exports = { 15 | compress, 16 | }; 17 | -------------------------------------------------------------------------------- /main/update.js: -------------------------------------------------------------------------------- 1 | const autoUpdater = require("electron-updater").autoUpdater; 2 | class Update { 3 | constructor() {} 4 | static check() { 5 | autoUpdater.checkForUpdatesAndNotify() 6 | } 7 | } 8 | module.exports = Update; 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tinyimage", 3 | "version": "1.2.8", 4 | "description": "一款压缩效率极高的图片压缩工具", 5 | "main": "main.js", 6 | "scripts": { 7 | "start": "electron .", 8 | "mac": "export CSC_IDENTITY_AUTO_DISCOVERY=false", 9 | "build": "electron-builder --x64 --publish never", 10 | "release": "electron-builder --x64 --publish always" 11 | }, 12 | "build": { 13 | "appId": "com.focusbe.tinyimage", 14 | "asar": false, 15 | "publish": [ 16 | { 17 | "provider": "github" 18 | } 19 | ], 20 | "asarUnpack": [ 21 | "node_modules/mozjpeg/", 22 | "node_modules/tinypngjs/", 23 | "node_modules/execa/", 24 | "node_modules/imagemin/", 25 | "renderer" 26 | ], 27 | "mac": { 28 | "category": "your.app.category.type", 29 | "icon": "assets/tinypng", 30 | "target": [ 31 | "zip", 32 | "dmg" 33 | ] 34 | }, 35 | "win": { 36 | "target": "nsis", 37 | "icon": "assets/tinypng" 38 | }, 39 | "nsis": { 40 | "oneClick": true, 41 | "allowToChangeInstallationDirectory": false 42 | }, 43 | "files": { 44 | "filter": [ 45 | "**/*", 46 | "!**/node_modules/*/{CHANGELOG.md,README.md,README,readme.md,readme,test,__tests__,tests,powered-test,example,examples,*.d.ts}", 47 | "!**/node_modules/.bin", 48 | "!**/*.{iml,o,hprof,orig,pyc,pyo,rbc,swp,csproj,sln,xproj}", 49 | "!**/._*", 50 | "!.editorconfig", 51 | "!package.json", 52 | "!README.md", 53 | "!yarn.lock", 54 | "!package-lock.json" 55 | ] 56 | }, 57 | "directories": { 58 | "output": "output" 59 | } 60 | }, 61 | "repository": { 62 | "type": "git", 63 | "url": "git+https://github.com/focusbe/tinyImage.git" 64 | }, 65 | "author": "刘鹏", 66 | "license": "ISC", 67 | "bugs": { 68 | "url": "https://github.com/focusbe/tinyImage/issues" 69 | }, 70 | "homepage": "https://github.com/focusbe/tinyImage#readme", 71 | "devDependencies": { 72 | "cross-env": "^7.0.3", 73 | "electron": "^24.1.2", 74 | "electron-builder": "^23.6.0" 75 | }, 76 | "dependencies": { 77 | "@electron/remote": "^2.0.9", 78 | "electron-updater": "^5.3.0", 79 | "fs-extra": "^8.1.0", 80 | "imagemin": "^8.0.1", 81 | "tinypngjs": "^1.2.10" 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /renderer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 |拖入文件夹或文件
16 |0/0 失败:0
22 | 23 |t |