手把手教你实战Flutter 桌面版-Tinypng(熊猫图片压缩)GUI工具

前言

Tinypng是一个在设计和技术界十分流行的图片压缩网站,但是它只有网页版,没有GUI。幸好的是它支持通过apikey直接运行api接口压缩图片,虽然业内已经有很多版本的GUI,Window,Mac都有,但是这几天学习Flutter Deskstop,正好可以用来实战。目前的版本已打包了macos版本及window版本。

Snip20220110_3.png

代码过程

实现选择文件

选择文件这块的实现,由于我本身是做iOS开发的,macOS原生开发其实也大同小异,但是为了兼容多端,我也懒得一个个写插件了,搜了下现成支持deskstop的插件发现file_picker这个插件完美支持我的想法,不管是window,mac,还是linux通通都支持。目前只支持选择jpg,png的文件,貌似webp和h265都是支持的,后期我可以加上。


void _pickFiles() async {

if (await controller.checkHaveApiKey() == false) {

_showSettingBottomSheet();

showToast("Please enter your TinyPNG Apikey",

textPadding: EdgeInsets.all(15));

return;

}

FilePickerResult? result =

await FilePicker.platform.pickFiles(allowMultiple: true);

if (result != null) {

List<File> files = result.paths.map((path) => File(path ?? "")).toList();

List<File> chooseFiles = [];

files.forEach((element) {

if (element.path.toLowerCase().endsWith("jpg") ||

element.path.toLowerCase().endsWith("jpeg") ||

element.path.toLowerCase().endsWith("png")) {

chooseFiles.add(element);

} else {

showToast('invalid image file', textPadding: EdgeInsets.all(15));

print("invalid image file : ${element.path}");

}

});

if (chooseFiles.isNotEmpty) {

controller.refreshWithFileList(chooseFiles);

}

} else {

showToast("Cancel Pick files", textPadding: EdgeInsets.all(15));

}

}

实现打开目录或者打开网址

这块一开始想了很久,iOS端是要通过urlLaunch跳转的,搜了下pub很多现场的库都只支持iOS和安卓对桌面不是很友好。突然灵机一动Swift脚本可以通过Process类直接运行terminal命令,dart是否有相关api支持?如果有的话打开目录 只需要一行命令 open xxx, 打开网址只需要open xxx.com。答案是显而易见的,dart也封装了 Proccess类,代码如下。


//打开图片压缩后目录

Process.run("open", [savePath]);

//打开跳转网址

Process.run("open", ["https://tinypng.com/developers"]);

实现上传原图文件到Tiny

这个没啥好说,看看http规则,直接撸代码即可。


Future<TinyImageInfo?> uploadOriginImage({required Uint8List? buffer}) async {

SharedPreferences prefs = await SharedPreferences.getInstance();

var apiKey = prefs.getString(KApiKey);

if (apiKey == null || apiKey.length == 0) {

return null;

}

var url = "api.tinify.com";

Uri uri = Uri.https(url, "/shrink");

var auth = "api:$apiKey";

var authData = base64Encode(utf8.encode(auth));

var authorizationHeader = "Basic " + authData;

var headers = {

"Accept": "application/json",

"Authorization": authorizationHeader,

};

try {

var response = await http.post(uri, headers: headers, body: buffer);

if (response.statusCode != 201) {

print("fail code is ${response.statusCode}");

return null;

} else {

var json = jsonDecode(utf8.decode(response.bodyBytes));

var jsonString = jsonEncode(json);

print("success json $jsonString");

return TinyImageInfo.fromJson(json);

}

} catch (e) {

print("catch upload error $e");

return null;

}

}

实现下载压缩后图片到自己目录

TinyPng上传原图成功而且压缩处理完成后会返回这样一串Json

{"input":{"size":84736,"type":"image/webp"},"output":{"size":68282,"type":"image/webp","width":658,"height":1009,"ratio":0.8058,"url":"https://api.tinify.com/output/avxq4rhjha1apfra92pzfnrcj2n0zdbx"}}

里面包含了压缩率,原图size,压缩后size,压缩后输出地址等等。有了这个json我们自然就能构建我们的UI了。


Future<bool> downloadOutputImage(TinyImageInfo imageInfo, String savePath,

{Function(int count, int total)? onReceiveProgress}) async {

String? url = imageInfo.output?.url;

String? type = imageInfo.output?.type;

if (url == null || type == null) {

return false;

}

Uri uri = Uri.parse(url);

var dio = Dio();

try {

var rsp = await dio.downloadUri(

uri,

savePath,

options: Options(

headers: {"Accept": type, "Content-Type": "application/json"},

),

onReceiveProgress: (count, total) {

onReceiveProgress?.call(count, total);

},

);

return rsp.statusCode == 200;

} catch (e) {

return false;

}

}

Mac应用权限问题

要配置一下这几个权限,不然应用会各种权限报错。


<key>com.apple.security.app-sandbox</key>

<false/>

<key>com.apple.security.cs.allow-jit</key>

<true/>

<key>com.apple.security.network.server</key>

<true/>

<key>com.apple.security.network.client</key>

<true/>

<key>com.apple.security.files.user-selected.read-write</key>

状态管理

我用了目前比较流行的Gex状态管理,只需要监听几个属性即可。


final PathProviderPlatform provider = PathProviderUtil.provider();

var taskList = <TinyImageInfoItemViewModel>[].obs;

var savePath = "".obs;

var apiKey = "".obs;

var taskCount = 0.obs;

var saveKb = 0.0.obs;

用到的三方库

项目地址

此项目完全开源,想学习的小伙伴可以去GitHub查看,有帮助到你们的麻烦给个Star哈。此项目基于Flutter 2.2.3开发,理论上是兼容更高的版本。(没有实测)

https://github.com/JerryFans/TinyPNG4Flutter

安装包

不想编译的可以直接用安装包

MacOS

dmg安装包

pkg安装包

Windows

安装包

未来

1、window版本打包 ☑️

2、linux版本? (貌似用的群体不多吧)

3、mac版本支持文件拖拽过去(看了mac AppKit文档,这个其实不是很难,只需要做个插件就行,未来会做好并开源)

4、上架AppStore提供给麻瓜用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容