Flutter图片压缩插件light_compressor的使用

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

Flutter图片压缩插件light_compressor的使用

插件概述

light_compressor 是一个强大且易于使用的Flutter视频压缩插件,基于 LightCompressor 库(用于Android)和 LightCompressor_iOS 库(用于iOS和macOS)。该插件可以生成具有修改后的宽度、高度和比特率的压缩MP4视频。其工作原理是减少极高的比特率,同时保持良好的视频质量,从而实现更小的文件大小。

Android-demo

安装与配置

添加依赖

首先,在您的pubspec.yaml文件中添加light_compressor作为依赖项:

dependencies:
  light_compressor: ^latest_version # 替换为最新版本号

平台特定配置

iOS 和 macOS

<project root>/ios/Runner/Info.plist文件中添加以下内容:

<key>NSPhotoLibraryUsageDescription</key>
<string>${PRODUCT_NAME} library Usage</string>

Android

AndroidManifest.xml中添加权限声明:

  • API < 29
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="28"
    tools:ignore="ScopedStorage" />
  • API >= 29
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
    android:maxSdkVersion="32"/>
  • API >= 33
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>

在项目级别的build.gradle文件中添加JitPack仓库:

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

在模块级别的build.gradle文件中添加依赖:

implementation 'com.github.AbedElazizShe:LightCompressor:1.3.2'

确保Kotlin版本不低于1.8.21,可以在项目级别的build.gradle文件中设置:

ext.kotlin_version = '1.8.21'

使用示例

以下是一个完整的示例应用,展示了如何使用light_compressor进行视频压缩,并展示压缩进度和结果。

import 'dart:async';
import 'dart:io';

import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:light_compressor/light_compressor.dart';

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

/// A widget that uses LightCompressor library to compress videos
class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late String _desFile;
  String? _displayedFile;
  late int _duration;
  String? _failureMessage;
  String? _filePath;
  bool _isVideoCompressed = false;

  final LightCompressor _lightCompressor = LightCompressor();

  @override
  Widget build(BuildContext context) => MaterialApp(
        theme: ThemeData(
          primaryColor: const Color(0xFF344772),
          primaryColorDark: const Color(0xFF002046),
        ),
        home: Scaffold(
          appBar: AppBar(
            title: const Text('Compressor Sample'),
            actions: [
              TextButton(
                child: const Text(
                  'Cancel',
                  style: TextStyle(color: Colors.white, fontSize: 18),
                ),
                onPressed: () => _lightCompressor.cancelCompression(),
              )
            ],
          ),
          body: Container(
            margin: const EdgeInsets.all(16),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                if (_filePath != null)
                  Text(
                    'Original size: ${_getVideoSize(file: File(_filePath!))}',
                    style: const TextStyle(fontSize: 16),
                  ),
                const SizedBox(height: 8),
                if (_isVideoCompressed)
                  Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        'Size after compression: ${_getVideoSize(file: File(_desFile))}',
                        style: const TextStyle(fontSize: 16),
                      ),
                      const SizedBox(height: 8),
                      Text(
                        'Duration: $_duration seconds',
                        style: const TextStyle(fontSize: 16, color: Colors.red),
                      ),
                    ],
                  ),
                const SizedBox(height: 16),
                Visibility(
                  visible: !_isVideoCompressed,
                  child: StreamBuilder<double>(
                    stream: _lightCompressor.onProgressUpdated,
                    builder: (BuildContext context,
                        AsyncSnapshot<dynamic> snapshot) {
                      if (snapshot.data != null && snapshot.data > 0) {
                        return Column(
                          children: [
                            LinearProgressIndicator(
                              minHeight: 8,
                              value: snapshot.data / 100,
                            ),
                            const SizedBox(height: 8),
                            Text(
                              '${snapshot.data.toStringAsFixed(0)}%',
                              style: const TextStyle(fontSize: 20),
                            )
                          ],
                        );
                      }
                      return const SizedBox.shrink();
                    },
                  ),
                ),
                const SizedBox(height: 24),
                if (_displayedFile != null)
                  Builder(
                    builder: (BuildContext context) => Container(
                      alignment: Alignment.center,
                      child: OutlinedButton(
                          onPressed: () => Navigator.push<dynamic>(
                                context,
                                MaterialPageRoute<dynamic>(
                                  builder: (_) => VideoPlayerScreen(_desFile),
                                ),
                              ),
                          child: const Text('Play Video')),
                    ),
                  ),
                Text(
                  _failureMessage ?? '',
                )
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton.extended(
            onPressed: () => _pickVideo(),
            label: const Text('Pick Video'),
            icon: const Icon(Icons.video_library),
            backgroundColor: const Color(0xFFA52A2A),
          ),
        ),
      );

  // Pick a video form device's storage
  Future<void> _pickVideo() async {
    _isVideoCompressed = false;

    final FilePickerResult? result = await FilePicker.platform.pickFiles(
      type: FileType.video,
    );

    final PlatformFile? file = result?.files.first;

    if (file == null) {
      return;
    }

    _filePath = file.path;

    setState(() {
      _failureMessage = null;
    });

    final String videoName =
        'MyVideo-${DateTime.now().millisecondsSinceEpoch}.mp4';

    final Stopwatch stopwatch = Stopwatch()..start();
    final Result response = await _lightCompressor.compressVideo(
      path: _filePath!,
      videoQuality: VideoQuality.medium,
      isMinBitrateCheckEnabled: false,
      video: Video(videoName: videoName),
      android: AndroidConfig(isSharedStorage: true, saveAt: SaveAt.Movies),
      ios: IOSConfig(saveInGallery: false),
    );

    stopwatch.stop();
    final Duration duration =
        Duration(milliseconds: stopwatch.elapsedMilliseconds);
    _duration = duration.inSeconds;

    if (response is OnSuccess) {
      setState(() {
        _desFile = response.destinationPath;
        _displayedFile = _desFile;
        _isVideoCompressed = true;
      });
    } else if (response is OnFailure) {
      setState(() {
        _failureMessage = response.message;
      });
    } else if (response is OnCancelled) {
      print(response.isCancelled);
    }
  }

  String _getVideoSize({required File file}) =>
      formatBytes(file.lengthSync(), 2);
}

// Helper function to format bytes into human-readable format
String formatBytes(int bytes, int decimals) {
  if (bytes <= 0) return "0 B";
  const suffixes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
  var i = (log(bytes) / log(1024)).floor();
  return ((bytes / pow(1024, i)).toStringAsFixed(decimals)) + ' ' + suffixes[i];
}

主要功能

  1. 选择视频:通过FilePicker从设备存储中选择视频。
  2. 压缩视频:调用compressVideo方法进行视频压缩,支持多种压缩质量选项。
  3. 显示进度:使用StreamBuilder实时显示压缩进度。
  4. 取消压缩:提供按钮以取消正在进行的压缩操作。
  5. 结果显示:压缩完成后显示原始和压缩后的文件大小及压缩时间,并提供播放压缩后视频的功能。

兼容性

  • Android SDK:最低API级别为24。
  • iOS:最低版本为11。
  • macOS:最低版本为10.15。

报告问题

如果您遇到任何问题,请提供以下信息以便更好地帮助您:

希望这个指南能帮助您顺利使用light_compressor插件进行视频压缩!如果有任何疑问或需要进一步的帮助,请随时提问。


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

1 回复

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


当然,下面是一个关于如何在Flutter项目中使用light_compressor插件进行图片压缩的示例代码。这个插件可以帮助你有效地压缩图片,减少其文件大小,而不显著影响图片质量。

首先,你需要在pubspec.yaml文件中添加light_compressor依赖项:

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

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

接下来,你可以在你的Flutter项目中使用light_compressor进行图片压缩。以下是一个完整的示例,包括从设备图库中选择图片、进行压缩并显示压缩后的图片:

import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:light_compressor/light_compressor.dart';
import 'dart:typed_data/uint8list.dart';
import 'dart:io';

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

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

class ImageCompressionScreen extends StatefulWidget {
  @override
  _ImageCompressionScreenState createState() => _ImageCompressionScreenState();
}

class _ImageCompressionScreenState extends State<ImageCompressionScreen> {
  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) {
      Uint8List? imageBytes = await _imageFile!.readAsBytes();

      if (imageBytes != null) {
        Uint8List compressedImageBytes = await LightCompressor.compressImage(
          imageBytes,
          quality: 80, // 质量(0-100),值越低,压缩率越高,但质量越低
        );

        File compressedImageFile = File('${_imageFile!.path}.jpg')
          ..writeAsBytesSync(compressedImageBytes);

        setState(() {
          _compressedImageFile = compressedImageFile;
        });
      }
    }
  }

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

在这个示例中,我们做了以下几件事:

  1. 使用image_picker插件从设备图库中选择图片。
  2. 使用light_compressor插件压缩选择的图片。
  3. 显示原始图片(如果压缩后的图片尚未生成)或压缩后的图片。

注意:

  • 你需要在android/app/src/main/AndroidManifest.xml中添加必要的权限来访问图库。
  • light_compressor插件支持多种图片格式(如JPEG、PNG等),但在这个示例中,为了简单起见,我们假设压缩后的图片为JPEG格式,并添加了一个.jpg扩展名。在实际应用中,你可能需要根据原始图片格式动态调整输出文件的扩展名。

希望这个示例能帮你顺利地在Flutter项目中使用light_compressor插件进行图片压缩。

回到顶部