fix: сделал сжатие для фоток при отправке. 80 мб фото это нечто

This commit is contained in:
Jganenokk
2026-07-24 13:13:33 +07:00
parent 59d06d725f
commit d258f462c4
9 changed files with 269 additions and 17 deletions
+22
View File
@@ -14,6 +14,12 @@ abstract class GalleryItem {
Future<Uint8List?> thumbnail(int size);
Future<File?> originFile();
Future<(int, int)?> dimensions();
Future<Uint8List?> encodeForUpload({
required int maxDimension,
required int quality,
});
static GalleryItem fromFile(File file) => _FileGalleryItem(file);
}
class PickedPhoto {
@@ -109,6 +115,16 @@ class _AssetGalleryItem implements GalleryItem {
@override
Future<File?> originFile() => asset.file;
@override
Future<Uint8List?> encodeForUpload({
required int maxDimension,
required int quality,
}) => asset.thumbnailDataWithSize(
ThumbnailSize(maxDimension, maxDimension),
format: ThumbnailFormat.jpeg,
quality: quality,
);
@override
Future<(int, int)?> dimensions() async {
if (asset.width > 0 && asset.height > 0) {
@@ -197,6 +213,12 @@ class _FileGalleryItem implements GalleryItem {
@override
Future<File?> originFile() async => file;
@override
Future<Uint8List?> encodeForUpload({
required int maxDimension,
required int quality,
}) async => null;
@override
Future<(int, int)?> dimensions() => imageFileDimensions(file);
}
+96
View File
@@ -0,0 +1,96 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:image/image.dart' as img;
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'gallery_source.dart';
const int kPhotoUploadLimitBytes = 5 * 1024 * 1024;
const int _photoMaxDimension = 2560;
const int _photoJpegQuality = 85;
const int _photoTargetBytes = 4700 * 1024;
const Set<String> _heicExtensions = {'.heic', '.heif'};
bool isHeicPath(String path) {
final dot = path.lastIndexOf('.');
if (dot < 0) return false;
return _heicExtensions.contains(path.substring(dot).toLowerCase());
}
Future<File> optimizePhotoForUpload(File source, {GalleryItem? item}) async {
final heic = isHeicPath(source.path);
var length = 0;
try {
length = await source.length();
} catch (_) {}
if (!heic && length > 0 && length <= kPhotoUploadLimitBytes) {
return source;
}
if (item != null) {
final native = await item.encodeForUpload(
maxDimension: _photoMaxDimension,
quality: _photoJpegQuality,
);
if (native != null) return writePhotoJpeg(native);
}
final fallback = await _encodeWithImagePackage(source);
return fallback ?? source;
}
Future<File?> _encodeWithImagePackage(File source) async {
Uint8List bytes;
try {
bytes = await source.readAsBytes();
} catch (_) {
return null;
}
final jpeg = await compute(_encodePhotoIsolate, (
bytes,
_photoMaxDimension,
_photoJpegQuality,
_photoTargetBytes,
));
if (jpeg == null) return null;
return writePhotoJpeg(jpeg);
}
Uint8List? _encodePhotoIsolate((Uint8List, int, int, int) args) {
final (bytes, maxDim, quality, target) = args;
final decoded = img.decodeImage(bytes);
if (decoded == null) return null;
final oriented = img.bakeOrientation(decoded);
final scaled = oriented.width > maxDim || oriented.height > maxDim
? img.copyResize(
oriented,
width: oriented.width >= oriented.height ? maxDim : null,
height: oriented.height > oriented.width ? maxDim : null,
interpolation: img.Interpolation.average,
)
: oriented;
var quality0 = quality;
var out = img.encodeJpg(scaled, quality: quality0);
while (out.lengthInBytes > target && quality0 > 40) {
quality0 -= 10;
out = img.encodeJpg(scaled, quality: quality0);
}
return out;
}
Future<File> writePhotoJpeg(Uint8List bytes) async {
final dir = await getTemporaryDirectory();
final file = File(
p.join(
dir.path,
'komet_photo_${DateTime.now().microsecondsSinceEpoch}.jpg',
),
);
await file.writeAsBytes(bytes, flush: true);
return file;
}