fix: сделал сжатие для фоток при отправке. 80 мб фото это нечто
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user