crop_image_handler.dart 3.48 KB
import 'dart:io';
import 'dart:typed_data';

import 'package:appframe/config/constant.dart';
import 'package:appframe/services/dispatcher.dart';
import 'package:dio/dio.dart';
import 'package:image/image.dart' as img;
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
import 'package:uuid/uuid.dart';

class CropImageHandler extends MessageHandler {
  @override
  Future<dynamic> handleMessage(params) async {
    if (params is! Map<String, dynamic>) {
      throw Exception('参数错误');
    }
    var url = params['url'] as String?;
    if (url == null || url.isEmpty) {
      throw Exception('参数错误');
    }
    var cropScale = params['cropScale'] as String?;
    if (cropScale == null || cropScale.isEmpty) {
      throw Exception('参数错误');
    }
    var scaleArray = ['16:9', '9:16', '4:3', '3:4', '5:4', '4:5', '1:1'];
    if (!scaleArray.contains(cropScale)) {
      throw Exception('参数错误');
    }

    try {
      String outputPath = await _cropImageByRatio(url, cropScale);
      return {'tempFilePath': '${Constant.localServerTempFileUrl}$outputPath'};
    } catch (e) {
      throw Exception('裁剪出错');
    }
  }

  Future<String> _cropImageByRatio(String url, String cropScale) async {
    if (url.startsWith(Constant.localServerUrl)) {
      url = url.replaceFirst(Constant.localServerUrl, '');
    }

    if (url.startsWith(Constant.localServerTemp)) {
      url = url.replaceFirst(Constant.localServerTemp, '');
    }

    String extension = path.extension(url);
    Directory cacheDirectory = await getApplicationCacheDirectory();

    File imageFile;

    // 对于远程图片,先下载到应用缓存目录,然后生成File
    if (url.startsWith('https://') || url.startsWith('http://')) {
      String tempFilePath = '${cacheDirectory.path}/${Uuid().v4()}$extension';
      Dio dio = Dio();
      Response resp = await dio.download(url, tempFilePath);
      if (resp.statusCode != 200) {
        throw Exception('下载图片失败');
      }
      dio.close(force: true);
      imageFile = File(tempFilePath);
    }
    // 对于本地路径,直接生成File
    else {
      imageFile = File(url);
    }

    // 1. 读取图片文件并解码
    Uint8List bytes = await imageFile.readAsBytes();
    img.Image originalImage = img.decodeImage(bytes)!;

    int originalWidth = originalImage.width;
    int originalHeight = originalImage.height;

    // 2. 计算目标裁剪区域
    var targetRatio = cropScale.split(":");
    double targetAspect = int.parse(targetRatio[0]) / int.parse(targetRatio[1]); // 例如 16/9
    double currentAspect = originalWidth / originalHeight;

    int cropWidth, cropHeight;

    if (currentAspect > targetAspect) {
      // 原图更宽,以高度为基准裁剪宽度
      cropHeight = originalHeight;
      cropWidth = (originalHeight * targetAspect).round();
    } else {
      // 原图更高,以宽度为基准裁剪高度
      cropWidth = originalWidth;
      cropHeight = (originalWidth / targetAspect).round();
    }

    // 3. 计算居中裁剪的起始点
    int startX = (originalWidth - cropWidth) ~/ 2;
    int startY = (originalHeight - cropHeight) ~/ 2;

    // 4. 执行裁剪
    img.Image croppedImage = img.copyCrop(originalImage, x: startX, y: startY, width: cropWidth, height: cropHeight);

    // 5. 保存裁剪后的图片
    String outputPath = '${cacheDirectory.path}/${Uuid().v4()}$extension';
    await File(outputPath).writeAsBytes(img.encodeJpg(croppedImage));
    return outputPath;
  }
}