Open vihatsoft opened 1 month ago
I face same issue, here is file.
I found it have a [0x20] in end of file. after [0xFF, 0xD9]. Is it possiable add error handle for this case ?
Use this. working with your image.
var info = ImageSizeData.fromBytes(file);
info.width
info.height
info.format
import 'dart:io';
import 'dart:typed_data';
import 'package:collection/collection.dart';
/// Image formats supported by Flutter. enum ImageFormat { /// A Portable Network Graphics format image. png,
/// A JPEG format image. /// /// This library does not support JPEG 2000. jpeg,
/// A WebP format image. webp,
/// A Graphics Interchange Format image. gif,
/// A Windows Bitmap format image. bmp, }
/// Provides details about image format information for raw compressed bytes /// of an image. abstract class ImageSizeData { /// Allows subclasses to be const. const ImageSizeData({ required this.format, required this.width, required this.height, }) : assert(width >= 0), assert(height >= 0);
/// Creates an appropriate [ImageSizeData] for the source bytes
, if possible.
///
/// Only supports image formats supported by Flutter.
factory ImageSizeData.fromBytes(file) {
var bytes = file.readAsBytesSync();
ByteData byteData = bytes.buffer.asByteData();
if (bytes.isEmpty) {
throw ArgumentError('bytes');
} else if (PngImageSizeData.matches(bytes)) {
return PngImageSizeData.(byteData);
} else if (GifImageSizeData.matches(bytes)) {
return GifImageSizeData.(byteData);
} else if (JpegImageSizeData.matches(bytes)) {
return JpegImageSizeData.fromBytes(file);
} else if (WebPImageSizeData.matches(bytes)) {
return WebPImageSizeData.(byteData);
} else if (BmpImageSizeData.matches(bytes)) {
return BmpImageSizeData.(byteData);
} else {
return BmpImageSizeData.(byteData);
}
}
/// The [ImageFormat] this instance represents. final ImageFormat format;
/// The width, in pixels, of the image. /// /// If the image is multi-frame, this is the width of the first frame. final int width;
/// The height, in pixels, of the image. /// /// If the image is multi-frame, this is the height of the first frame. final int height;
/// The estimated size of the image in bytes.
///
/// The withMipmapping
parameter controls whether to account for mipmapping
/// when decompressing the image. Flutter will use this when possible, at the
/// cost of slightly more memory usage.
int decodedSizeInBytes({bool withMipmapping = true}) {
if (withMipmapping) {
return (width height 4.3).ceil();
}
return width height 4;
}
}
/// The [ImageSizeData] for a PNG image. class PngImageSizeData extends ImageSizeData { PngImageSizeData._(ByteData data) : super( format: ImageFormat.png, width: data.getUint32(16, Endian.big), height: data.getUint32(20, Endian.big), );
/// Returns true if bytes
starts with the expected header for a PNG image.
static bool matches(Uint8List bytes) {
return bytes.lengthInBytes > 20 &&
bytes[0] == 0x89 &&
bytes[1] == 0x50 &&
bytes[2] == 0x4E &&
bytes[3] == 0x47 &&
bytes[4] == 0x0D &&
bytes[5] == 0x0A &&
bytes[6] == 0x1A &&
bytes[7] == 0x0A;
}
}
/// The [ImageSizeData] for a GIF image. class GifImageSizeData extends ImageSizeData { GifImageSizeData._(ByteData data) : super( format: ImageFormat.gif, width: data.getUint16(6, Endian.little), height: data.getUint16(8, Endian.little), );
/// Returns true if bytes
starts with the expected header for a GIF image.
static bool matches(Uint8List bytes) {
return bytes.lengthInBytes > 8 &&
bytes[0] == 0x47 &&
bytes[1] == 0x49 &&
bytes[2] == 0x46 &&
bytes[3] == 0x38 &&
(bytes[4] == 0x37 || bytes[4] == 0x39) // 7 or 9
&&
bytes[5] == 0x61;
}
}
/// The [ImageSizeData] for a JPEG image. /// /// This library does not support JPEG2000 images. class JpegImageSizeData extends ImageSizeData { JpegImageSizeData._({required super.width, required super.height}) : super( format: ImageFormat.jpeg, );
factory JpegImageSizeData._fromBytes(file) { ByteData data = file.readAsBytesSync().buffer.asByteData(); int start = 2; BlockEntity? block; var orientation = 1;
FileInput input = FileInput(File(file.path));
block = _getBlockSync(input, start);
if (block == null) {
throw Exception('Invalid jpeg file');
}
// Check for App1 block
if (block.type == 0xE1) {
final app1BlockData = input.getRange(
start,
block.start + block.length,
);
final exifOrientation = _getOrientation(app1BlockData);
if (exifOrientation != null) {
orientation = exifOrientation;
}
}
final needRotate = [5, 6, 7, 8].contains(orientation);
int index = 4; // Skip the first header bytes (already validated).
index += data.getUint16(index, Endian.big);
while (index < data.lengthInBytes) {
if (data.getUint8(index) != 0xFF) {
// Start of block
throw StateError('Invalid JPEG file${data.getUint8(index)}');
}
// if (data.getUint8(index + 1) == 0xC0) {
if ([0xC0, 0xC1, 0xC2].contains(data.getUint8(index + 1))) {
// Start of frame 0
if (needRotate) {
return JpegImageSizeData._(
height: data.getUint16(index + 7, Endian.big),
width: data.getUint16(index + 5, Endian.big),
);
} else {
return JpegImageSizeData._(
height: data.getUint16(index + 5, Endian.big),
width: data.getUint16(index + 7, Endian.big),
);
}
}
index += 2;
index += data.getUint16(index, Endian.big);
}
return JpegImageSizeData._(
height: 0,
width: 0,
);
}
/// Returns true if bytes
starts with the expected header for a JPEG image.
static bool matches(Uint8List bytes) {
return bytes.lengthInBytes > 12 &&
bytes[0] == 0xFF &&
bytes[1] == 0xD8 &&
bytes[2] == 0xFF;
}
}
/// The [ImageSizeData] for a WebP image. class WebPImageSizeData extends ImageSizeData {
WebPImageSizeData._(ByteData data) : super( format: ImageFormat.webp, width: data.getUint16(26, Endian.little), height: data.getUint16(28, Endian.little), );
/// Returns true if bytes
starts with the expected header for a WebP image.
static bool matches(Uint8List bytes) {
// ByteData bytes = file.readAsBytesSync().buffer.asByteData();
return bytes.lengthInBytes > 28 &&
bytes[0] == 0x52 // R
&&
bytes[1] == 0x49 // I
&&
bytes[2] == 0x46 // F
&&
bytes[3] == 0x46 // F
&&
bytes[8] == 0x57 // W
&&
bytes[9] == 0x45 // E
&&
bytes[10] == 0x42 // B
&&
bytes[11] == 0x50; // P
} }
/// The [ImageSizeData] for a BMP image. class BmpImageSizeData extends ImageSizeData { BmpImageSizeData._(ByteData data) : super( format: ImageFormat.bmp, width: data.getInt32(18, Endian.little), height: data.getInt32(22, Endian.little));
/// Returns true if bytes
starts with the expected header for a WebP image.
static bool matches(Uint8List bytes) {
return bytes.lengthInBytes > 22 && bytes[0] == 0x42 && bytes[1] == 0x4D;
}
}
class BlockEntity { /// The block of jpeg format. BlockEntity(this.type, this.length, this.start);
/// The type of the block. int type;
/// The length of the block. int length;
/// Start of offset int start;
/// Error block. static BlockEntity error = BlockEntity(-1, -1, -1);
@override String toString() { return "BlockEntity (type:$type, length:$length)"; } }
BlockEntity _createBlock(
List
return BlockEntity(typeInt, blockLength, blockStart); }
int convertRadix16ToInt(List
for (final i in list) { sb.write(i.toRadixString(16).padLeft(2, '0')); } final numString = sb.toString(); return int.tryParse(numString, radix: 16) ?? 0; }
BlockEntity? _getBlockSync(FileInput input, int blockStart) { try { final blockInfoList = input.getRange(blockStart, blockStart + 4);
if (blockInfoList[0] != 0xFF) {
return null;
}
final blockSizeList = input.getRange(blockStart + 2, blockStart + 4);
return _createBlock(blockSizeList, blockStart, blockInfoList);
} catch (e) { return null; } }
int? _getOrientation(List
// app1 block buffer: // header (2 bytes) // length (2 bytes) // exif header (6 bytes) // exif for little endian (2 bytes), 0x4d4d is for big endian, 0x4949 is for little endian // tag mark (2 bytes) // offset first IFD (4 bytes) // IFD data : // number of entries (2 bytes) // for each entry: // exif tag (2 bytes) // data format (2 bytes), 1 = unsigned byte, 2 = ascii, 3 = unsigned short, 4 = unsigned long, 5 = unsigned rational, 6 = signed byte, 7 = undefined, 8 = signed short, 9 = signed long, 10 = signed rational // number of components (4 bytes) // value (4 bytes) // padding (0 ~ 3 bytes, depends on data format) // So, the IFD data starts at offset 14.
// Check app1 block exif info is valid if (app1blockData.length < 14) { return null; }
// Check app1 block exif info is valid final exifIdentifier = app1blockData.sublist(4, 10);
const listEquality = ListEquality();
if (!listEquality .equals(exifIdentifier, [0x45, 0x78, 0x69, 0x66, 0x00, 0x00])) { return null; }
final littleEndian = app1blockData[10] == 0x49;
int getNumber(int start, int end) { final numberList = app1blockData.sublist(start, end); return convertRadix16ToInt(numberList, reverse: littleEndian); }
// Get idf byte var idf0Start = 18; final tagEntryCount = getNumber(idf0Start, idf0Start + 2);
var currentIndex = idf0Start + 2;
for (var i = 0; i < tagEntryCount; i++) { final tagType = getNumber(currentIndex, currentIndex + 2);
if (tagType == 0x0112) {
return getNumber(currentIndex + 8, currentIndex + 10);
}
// every tag length is 0xC bytes
currentIndex += 0xC;
}
return null; }
class FileInput { /// {@macro image_size_getter.file_input} const FileInput(this.file);
final File file;
List
int get length => file.lengthSync();
bool exists() { return file.existsSync(); } }
class FileUtils { /// {@macro image_size_getter.FileUtils} FileUtils(this.file);
/// The file. File file;
/// {@macro image_size_getter.FileUtils.getRangeSync}
Future<List
List
I'm having a similar problem with the attached image. The solution above by @vihatsoft looks like it might work, but I would need to modify it quite a bit for JPEG images as our solution needs to work with bytes as well as files. I would also prefer a solution integrated into the current library as we've been using it for some time.
I am also having the same issues when using camera photos
Content
when select jpg image get error for some files. final size = ImageSizeGetter.getSize(FileInput(file));
why get error with jpg file that take from camera?