Flutter图片压缩插件simple_native_image_compress的使用

发布于 1周前 作者 sinazl 来自 Flutter

Flutter图片压缩插件simple_native_image_compress的使用

简介

simple_native_image_compress 是一个基于 Rust 编写的简单原生图片压缩库,适用于 Flutter。它利用了 flutter_rust_bridgeimagecargokitexif-rsanyhow

为什么选择这个插件?

  1. Dart 中的图片压缩速度较慢,即使使用 isolate。
  2. 其他原生库不支持 Windows 和 Linux 的图片压缩。

功能概述

  • 如果提供了一个图片文件路径,它会调整大小并返回 Jpeg/WebP 图片作为 Uint8List
  • 不支持 Web 端(WASM),因为开发者使用 Angular 而非 Flutter 开发 Web 应用。

支持的输出格式

  • Jpeg
  • WebP(仅无损)
  • AVIF(仅有损,可设置压缩速度)

采样滤波器类型

  • Nearest:最近邻
  • Triangle:线性滤波(默认)
  • CatmullRom:三次滤波
  • Gaussian:高斯滤波
  • Lanczos3:Lanczos 滤波,窗口为 3

更多关于采样滤波器的信息可以参考 image crate doc

示例代码

以下是一个完整的示例应用,展示了如何使用 simple_native_image_compress 插件进行图片压缩和保存。

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:simple_native_image_compress/simple_native_image_compress.dart';
import 'package:image_picker/image_picker.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter_avif/flutter_avif.dart';

Future<void> main() async {
  await NativeImageCompress.init();
  runApp(const MaterialApp(home: MyApp()));
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  Duration _duration = const Duration();
  final _picker = ImagePicker();
  Uint8List? _bytes;
  final _compressFormat = CompressFormat.avif;

  Future<void> _compressImage() async {
    String filePath = '';
    if (Platform.isMacOS) {
      final res = await FilePicker.platform.pickFiles(type: FileType.image, allowMultiple: false);
      if (res == null || res.files.isEmpty) return;
      filePath = res.files[0].path!;
    } else {
      final file = await _picker.pickImage(source: ImageSource.gallery);
      if (file == null) return;
      filePath = file.path;
    }
    try {
      final startTime = DateTime.now();
      final bytes = await ImageCompress.contain(
        filePath: filePath,
        compressFormat: _compressFormat,
        samplingFilter: FilterType.lanczos3,
      );
      final endTime = DateTime.now();
      setState(() {
        _bytes = bytes;
        _duration = endTime.difference(startTime);
      });
    } catch (e) {
      if (!mounted) return;
      showDialog<void>(
        context: context,
        builder: (_) => AlertDialog(
          title: const Text('Error Occured', style: TextStyle(color: Colors.red)),
          content: Text(e.toString()),
          actions: [
            Center(
              child: TextButton(
                style: TextButton.styleFrom(foregroundColor: Colors.white, backgroundColor: Colors.blue),
                onPressed: Navigator.of(context).pop,
                child: const Text('Ok'),
              ),
            ),
          ],
        ),
      );
    }
  }

  Future<void> _saveImage() async {
    if (Platform.isMacOS) return;
    String? outputFilePath = await FilePicker.platform.saveFile(dialogTitle: 'Please select an output file location', type: FileType.image);
    if (outputFilePath == null) return;
    final tempFile = File(outputFilePath);
    await tempFile.create(recursive: true);
    RandomAccessFile raf = tempFile.openSync(mode: FileMode.write);
    try {
      raf.writeFromSync(_bytes!);
    } catch (e) {
      rethrow;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Plugin example app')),
      body: SafeArea(
        child: Column(
          children: [
            const SizedBox(height: 20),
            ElevatedButton(onPressed: _compressImage, child: const Text('Choose an image')),
            const SizedBox(height: 20),
            Text('(${kDebugMode ? 'DEBUG' : 'RELEASE'}) mode time taken: ${_duration.inMilliseconds} ms'),
            const SizedBox(height: 10),
            Expanded(
              child: _bytes != null
                  ? InkWell(
                      onSecondaryTap: _saveImage,
                      child: _compressFormat == CompressFormat.avif
                          ? AvifImage.memory(_bytes!, fit: BoxFit.contain)
                          : Image.memory(_bytes!))
                  : Container(),
            ),
          ],
        ),
      ),
    );
  }
}

默认值

  • 宽度和高度的默认值是 1024 px
  • Jpeg/AVIF 质量的默认值是 80(WebP 质量无效)
  • 采样滤波器的默认值是 FilterType.Triangle
  • AVIF 压缩速度的默认值是 10

通过以上内容,您可以快速上手使用 simple_native_image_compress 插件进行图片压缩,并根据需要调整参数以满足您的需求。


更多关于Flutter图片压缩插件simple_native_image_compress的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter图片压缩插件simple_native_image_compress的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用simple_native_image_compress插件进行图片压缩的示例代码。这个插件利用原生平台(iOS和Android)的压缩能力,提供了高效的图片压缩功能。

1. 添加依赖

首先,在你的pubspec.yaml文件中添加simple_native_image_compress依赖:

dependencies:
  flutter:
    sdk: flutter
  simple_native_image_compress: ^latest_version  # 请替换为最新版本号

然后运行flutter pub get来安装依赖。

2. 导入插件

在你的Dart文件中导入插件:

import 'package:simple_native_image_compress/simple_native_image_compress.dart';

3. 使用插件进行图片压缩

下面是一个完整的示例,展示了如何从文件选择器中选择图片,然后使用simple_native_image_compress插件进行压缩,并显示压缩前后的图片大小:

import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:simple_native_image_compress/simple_native_image_compress.dart';
import 'dart:io';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ImageCompressDemo(),
    );
  }
}

class ImageCompressDemo extends StatefulWidget {
  @override
  _ImageCompressDemoState createState() => _ImageCompressDemoState();
}

class _ImageCompressDemoState extends State<ImageCompressDemo> {
  File? _imageFile;
  File? _compressedImageFile;

  final ImagePicker _picker = ImagePicker();

  Future<void> _pickImage() async {
    final pickedFile = await _picker.pickImage(source: ImageSource.gallery);

    if (pickedFile != null) {
      setState(() {
        _imageFile = File(pickedFile.path);
      });

      _compressImage();
    }
  }

  Future<void> _compressImage() async {
    if (_imageFile != null) {
      final result = await SimpleNativeImageCompress.compressImage(
        _imageFile!.path,
        quality: 80, // 设置压缩质量,范围0-100
      );

      if (result != null) {
        setState(() {
          _compressedImageFile = File(result.path);
        });

        // 打印压缩前后的文件大小
        print('Original size: ${_imageFile!.lengthSync()} bytes');
        print('Compressed size: ${_compressedImageFile!.lengthSync()} bytes');
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Image Compress Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
              onPressed: _pickImage,
              child: Text('Pick Image'),
            ),
            SizedBox(height: 20),
            if (_imageFile != null)
              Image.file(_imageFile!),
            SizedBox(height: 20),
            if (_compressedImageFile != null)
              Image.file(_compressedImageFile!),
          ],
        ),
      ),
    );
  }
}

4. 注意事项

  • 确保你已经在AndroidManifest.xmlInfo.plist中添加了必要的权限,以便访问设备的存储。
  • 压缩质量参数quality是一个介于0到100之间的整数,值越低表示压缩率越高,但图片质量也会相应下降。
  • 压缩后的图片会保存在应用的临时目录中,你可以根据需要将其移动到其他位置或进行进一步处理。

通过上述代码,你可以轻松地在Flutter应用中使用simple_native_image_compress插件进行图片压缩。

回到顶部