Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class CompressFileHandler(private val call: MethodCall, result: MethodChannel.Re
val formatHandler = FormatRegister.findFormat(format)
if (formatHandler == null) {
log("No support format.")
reply(null)
replyError("unsupported_format", "No handler for format=$format")
return@execute
}
val outputStream = ByteArrayOutputStream()
Expand Down Expand Up @@ -61,7 +61,7 @@ class CompressFileHandler(private val call: MethodCall, result: MethodChannel.Re
reply(outputStream.toByteArray())
} catch (e: Exception) {
if (ImageCompressPlugin.showLog) e.printStackTrace()
reply(null)
replyError("unknown", e.message ?: "compress failed")
} finally {
outputStream.close()
}
Expand All @@ -86,7 +86,7 @@ class CompressFileHandler(private val call: MethodCall, result: MethodChannel.Re
val formatHandler = FormatRegister.findFormat(format)
if (formatHandler == null) {
log("No support format.")
reply(null)
replyError("unsupported_format", "No handler for format=$format")
return@execute
}
var outputStream: OutputStream? = null
Expand Down Expand Up @@ -119,7 +119,7 @@ class CompressFileHandler(private val call: MethodCall, result: MethodChannel.Re
reply(targetPath)
} catch (e: Exception) {
if (ImageCompressPlugin.showLog) e.printStackTrace()
reply(null)
replyError("unknown", e.message ?: "compress failed")
} finally {
outputStream?.close()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class CompressListHandler(private val call: MethodCall, result: MethodChannel.Re
val formatHandler = FormatRegister.findFormat(format)
if (formatHandler == null) {
log("No support format.")
reply(null)
replyError("unsupported_format", "No handler for format=$format")
return@execute
}
val targetRotate = rotate + exifRotate
Expand All @@ -53,10 +53,10 @@ class CompressListHandler(private val call: MethodCall, result: MethodChannel.Re
} catch (e: CompressError) {
log(e.message)
if (ImageCompressPlugin.showLog) e.printStackTrace()
reply(null)
replyError("encode_failed", e.message ?: "compress failed")
} catch (e: Exception) {
if (ImageCompressPlugin.showLog) e.printStackTrace()
reply(null)
replyError("unknown", e.message ?: "compress failed")
} finally {
outputStream.close()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
if ([ImageCompressPlugin showLog]) {
NSLog(@"Input file could not be read (path=%@)", path);
}
result(nil);
result([FlutterError errorWithCode:@"io_failed"
message:[NSString stringWithFormat:@"Input file could not be read (path=%@)", path]
details:nil]);
return;
}

Expand All @@ -54,7 +56,9 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
if ([ImageCompressPlugin showLog]) {
NSLog(@"Input file is not a decodable image (mime=%@, path=%@)", imageType, path);
}
result(nil);
result([FlutterError errorWithCode:@"decode_failed"
message:[NSString stringWithFormat:@"Input file is not a decodable image (mime=%@, path=%@)", imageType, path]
details:nil]);
return;
}

Expand All @@ -75,7 +79,9 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
}

if (data == nil) {
result(nil);
result([FlutterError errorWithCode:@"encode_failed"
message:@"Encoder returned no data"
details:nil]);
return;
}
result([FlutterStandardTypedData typedDataWithBytes:data]);
Expand Down Expand Up @@ -103,7 +109,9 @@ - (void)handleCompressFileToFile:(FlutterMethodCall *)call result:(FlutterResult
if ([ImageCompressPlugin showLog]) {
NSLog(@"Input file could not be read (path=%@)", path);
}
result(nil);
result([FlutterError errorWithCode:@"io_failed"
message:[NSString stringWithFormat:@"Input file could not be read (path=%@)", path]
details:nil]);
return;
}

Expand All @@ -124,7 +132,9 @@ - (void)handleCompressFileToFile:(FlutterMethodCall *)call result:(FlutterResult
if ([ImageCompressPlugin showLog]) {
NSLog(@"Input file is not a decodable image (mime=%@, path=%@)", imageType, path);
}
result(nil);
result([FlutterError errorWithCode:@"decode_failed"
message:[NSString stringWithFormat:@"Input file is not a decodable image (mime=%@, path=%@)", imageType, path]
details:nil]);
return;
}

Expand All @@ -141,7 +151,9 @@ - (void)handleCompressFileToFile:(FlutterMethodCall *)call result:(FlutterResult
}

if (data == nil) {
result(nil);
result([FlutterError errorWithCode:@"encode_failed"
message:@"Encoder returned no data"
details:nil]);
return;
}
NSURL *targetURL = [[NSURL alloc] initFileURLWithPath:targetPath];
Expand All @@ -161,7 +173,13 @@ - (void)handleCompressFileToFile:(FlutterMethodCall *)call result:(FlutterResult
}
}
BOOL success = [data writeToURL:targetURL atomically:YES];
result(success ? targetPath : nil);
if (success) {
result(targetPath);
} else {
result([FlutterError errorWithCode:@"io_failed"
message:[NSString stringWithFormat:@"Failed to write compressed data to %@", targetPath]
details:nil]);
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
NSData *compressedData = [CompressHandler compressWithData:data minWidth:minWidth minHeight:minHeight quality:quality rotate:rotate format:formatType];

if (compressedData == nil) {
result(nil);
result([FlutterError errorWithCode:@"encode_failed"
message:@"Encoder returned no data"
details:nil]);
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ class FlutterImageCompressCommon extends FlutterImageCompressPlatform {
FlutterImageCompressPlatform.instance = FlutterImageCompressCommon();
}

/// Wrap [MethodChannel.invokeMethod] so native `result.error(...)` /
/// `replyError(...)` calls surface as [CompressError] on the Dart side
/// instead of leaking as [PlatformException] or (worse) a silent null
/// return type-cast failure. The native code carries a stable failure
/// code (see [CompressError.code]) and a human message.
Future<T?> _invoke<T>(String method, [dynamic args]) async {
try {
return await _channel.invokeMethod<T>(method, args);
} on PlatformException catch (e) {
throw CompressError(e.message ?? e.code, code: e.code);
}
}

@override
Future<typed_data.Uint8List?> compressAssetImage(String assetName,
{int minWidth = 1920,
Expand All @@ -51,10 +64,7 @@ class FlutterImageCompressCommon extends FlutterImageCompressPlatform {
bool autoCorrectionAngle = true,
CompressFormat format = CompressFormat.jpeg,
bool keepExif = false}) async {
final support = await _validator.checkSupportPlatform(format);
if (!support) {
return null;
}
await _validator.checkSupportPlatform(format);
final img = AssetImage(assetName);
const config = ImageConfiguration();
final AssetBundleImageKey key = await img.obtainKey(config);
Expand Down Expand Up @@ -94,11 +104,8 @@ class FlutterImageCompressCommon extends FlutterImageCompressPlatform {
if (!File(path).existsSync()) {
throw CompressError('Image file does not exist in $path.');
}
final support = await _validator.checkSupportPlatform(format);
if (!support) {
return null;
}
final result = await _channel.invokeMethod('compressWithFile', [
await _validator.checkSupportPlatform(format);
final result = await _invoke<typed_data.Uint8List>('compressWithFile', [
path,
minWidth,
minHeight,
Expand Down Expand Up @@ -126,11 +133,8 @@ class FlutterImageCompressCommon extends FlutterImageCompressPlatform {
if (image.isEmpty) {
throw CompressError('The image is empty.');
}
final support = await _validator.checkSupportPlatform(format);
if (!support) {
throw UnsupportedError('The image type $format is not supported.');
}
final result = await _channel.invokeMethod('compressWithList', [
await _validator.checkSupportPlatform(format);
final result = await _invoke<typed_data.Uint8List>('compressWithList', [
image,
minWidth,
minHeight,
Expand All @@ -141,6 +145,12 @@ class FlutterImageCompressCommon extends FlutterImageCompressPlatform {
keepExif,
inSampleSize,
]);
// Native failures now surface via `_invoke` as `CompressError`, so a null
// result here means the native side responded with `success(null)` — which
// shouldn't happen after Step 1. Guard defensively to avoid a TypeError.
if (result == null) {
throw CompressError('Compress returned no data');
}
return result;
}

Expand Down Expand Up @@ -173,11 +183,8 @@ class FlutterImageCompressCommon extends FlutterImageCompressPlatform {
throw CompressError('Target path and source path cannot be the same.');
}
_validator.checkFileNameAndFormat(targetPath, format);
final support = await _validator.checkSupportPlatform(format);
if (!support) {
return null;
}
final String? result = await _channel.invokeMethod(
await _validator.checkSupportPlatform(format);
final String? result = await _invoke<String>(
'compressWithFileAndGetFile',
[
path,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ class FlutterImageCompressMacos extends FlutterImageCompressPlatform {
}
}

/// See [FlutterImageCompressCommon._invoke] for rationale — native
/// `FlutterError` results become [CompressError] on the Dart side.
Future<T?> _invoke<T>(String method, [dynamic args]) async {
try {
return await _channel.invokeMethod<T>(method, args);
} on PlatformException catch (e) {
throw CompressError(e.message ?? e.code, code: e.code);
}
}

@override
Future<XFile?> compressAndGetFile(
String path,
Expand All @@ -34,7 +44,7 @@ class FlutterImageCompressMacos extends FlutterImageCompressPlatform {
}) async {
await checkSupport(format);

final dstPath = await _channel.invokeMethod('compressAndGetFile', {
final dstPath = await _invoke<String>('compressAndGetFile', {
'path': path,
'targetPath': targetPath,
'minWidth': minWidth,
Expand Down Expand Up @@ -99,7 +109,7 @@ class FlutterImageCompressMacos extends FlutterImageCompressPlatform {
}) async {
await checkSupport(format);

final result = await _channel.invokeMethod('compressWithFile', {
final result = await _invoke<Uint8List>('compressWithFile', {
'path': path,
'minWidth': minWidth,
'minHeight': minHeight,
Expand Down Expand Up @@ -133,7 +143,7 @@ class FlutterImageCompressMacos extends FlutterImageCompressPlatform {
}) async {
await checkSupport(format);

final result = await _channel.invokeMethod<Uint8List>('compressWithList', {
final result = await _invoke<Uint8List>('compressWithList', {
'list': image,
'minWidth': minWidth,
'minHeight': minHeight,
Expand All @@ -146,7 +156,7 @@ class FlutterImageCompressMacos extends FlutterImageCompressPlatform {
});

if (result == null) {
throw Exception('Compress failed');
throw CompressError('Compress failed');
}

return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,9 @@ class Compressor {
}

if (!ok) {
result(nil)
result(FlutterError(code: "encode_failed",
message: "Failed to encode compressed image to \(path)",
details: nil))
return
}

Expand All @@ -418,7 +420,9 @@ class Compressor {
}

if (!ok) {
result(nil)
result(FlutterError(code: "encode_failed",
message: "Failed to encode compressed image",
details: nil))
return
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ class FlutterImageCompressOhos extends FlutterImageCompressPlatform {
}
}

/// See [FlutterImageCompressCommon._invoke] for rationale — native
/// `result.error(...)` becomes [CompressError] on the Dart side.
Future<T?> _invoke<T>(String method, [dynamic args]) async {
try {
return await _channel.invokeMethod<T>(method, args);
} on PlatformException catch (e) {
throw CompressError(e.message ?? e.code, code: e.code);
}
}

@override
Future<XFile?> compressAndGetFile(
String path,
Expand All @@ -34,7 +44,7 @@ class FlutterImageCompressOhos extends FlutterImageCompressPlatform {
}) async {
await checkSupport(format);

final dstPath = await _channel.invokeMethod('compressAndGetFile', {
final dstPath = await _invoke<String>('compressAndGetFile', {
'path': path,
'targetPath': targetPath,
'minWidth': minWidth,
Expand Down Expand Up @@ -101,7 +111,7 @@ class FlutterImageCompressOhos extends FlutterImageCompressPlatform {
}) async {
await checkSupport(format);

final result = await _channel.invokeMethod('compressWithFile', {
final result = await _invoke<Uint8List>('compressWithFile', {
'path': path,
'minWidth': minWidth,
'minHeight': minHeight,
Expand Down Expand Up @@ -136,7 +146,7 @@ class FlutterImageCompressOhos extends FlutterImageCompressPlatform {
}) async {
await checkSupport(format);

final result = await _channel.invokeMethod<Uint8List>('compressWithList', {
final result = await _invoke<Uint8List>('compressWithList', {
'list': image,
'minWidth': minWidth,
'minHeight': minHeight,
Expand All @@ -150,7 +160,7 @@ class FlutterImageCompressOhos extends FlutterImageCompressPlatform {
});

if (result == null) {
throw Exception('Compress failed');
throw CompressError('Compress failed');
}

return result;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
class CompressError extends Error {
CompressError(this.message);
CompressError(this.message, {this.code});

final String message;

/// Optional machine-readable failure code emitted by the native side.
///
/// Stable values: `unsupported_format`, `decode_failed`, `encode_failed`,
/// `io_failed`, `unknown`. Unknown codes are passed through verbatim so
/// forward-compat callers can still branch on them without a plugin bump.
final String? code;

@override
String toString() => 'CompressError: $message';
String toString() => code == null
? 'CompressError: $message'
: 'CompressError($code): $message';
}
Loading