美文网首页Flutter
手把手教你实战Flutter 桌面版-Tinypng(熊猫图片压

手把手教你实战Flutter 桌面版-Tinypng(熊猫图片压

作者: JerryFans | 来源:发表于2022-01-09 15:44 被阅读0次

    前言

    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提供给麻瓜用

    相关文章

      网友评论

        本文标题:手把手教你实战Flutter 桌面版-Tinypng(熊猫图片压

        本文链接:https://www.haomeiwen.com/subject/ovhbyctx.html