Convert a UInt8List to Image in flutter/dart-ui - dart

I have this scenario where I have to get a UInt8List from an image like that:
List stuff = image.toByteData().buffer.asUInt8List()
Do some manipulations and get back to a Image.
I've tried the following:
List stuff = image.toByteData().buffer.asUInt8List()
ui.decodeImageFromList(stuff, (image){
// do stuff with image
});
But I keep getting this Exception:
E/flutter (10201): [ERROR:flutter/lib/ui/painting/codec.cc(97)] Failed decoding image. Data is either invalid, or it is encoded using an unsupported format.
E/flutter (10201): [ERROR:flutter/shell/common/shell.cc(186)] Dart Error: Unhandled exception:
...
Take notice that even without any changes in the List the exception is thrown. How can I make the list have a encodable format?

You can use memory image as given below, for the direct byte rendering
child: Image.memory(Uint8List bytes);

You can use MemoryImage class to convert a Uint8List to image.
var _image = MemoryImage(image);
You can find more details about this class here

ui.Image can turn itself into an RGBA bitmap (or PNG), but cannot convert itself back from a bitmap. (The reason for this is that there isn't any way to tell the image codec things like the color depth, geometry, etc.) The solution is to add a BMP file header onto the front of your bitmap, where you can describe those missing things, then pass that to instantiateImageCodec. See this answer, but note that in that case the bitmap in question had a strange packed color map. In your case of 32 bit RGBA, the header would be even simpler.

use this.
static Future<ui.Image> bytesToImage(Uint8List imgBytes) async{
ui.Codec codec = await ui.instantiateImageCodec(imgBytes);
ui.FrameInfo frame = await codec.getNextFrame();
return frame.image;
}

this method worked for me, got the reference from another answer to a similar kind of problem at stackoverflow here
Future<ui.Image> loadImage(Uint8List img) async {
final Completer<ui.Image> completer = Completer();
ui.decodeImageFromList(img, (ui.Image img) {
return completer.complete(img);
});
return completer.future;
}

Here is a tested code for you.
final directory = await getApplicationDocumentsDirectory();
final pathOfImage = await File('${directory.path}/legendary.png').create();
final Uint8List bytes = stuff.buffer.asUint8List();
await pathOfImage.writeAsBytes(bytes);
final Uint8List bytes = stuff.buffer.asUint8List();
Here, stuff refers to the Uint8List that should be converted to an image.

Related

Converting object to an encodable object failed: Instance of 'Future<dynamic>'

I am trying to get base64 string from image file. When I am using following method
Future convertBase64(file) async{
List<int> imageBytes = fileImage.readAsBytesSync();
String base64Image = await 'data:image/png;base64,' + base64Encode(imageBytes);
// print('length of image bytes ${base64Image.length}');
return base64Image;
}
It shows me an error :
exception---- Converting object to an encodable object failed: Instance of 'Future<dynamic>'
If I use without future it directly pass to next step without converting to base64 String. It usually takes time to convert.
The variable fileImage doesn't seem to match the variable file passed to the function. Might this be the one causing the issue?
I'm curious on why the need to call await on a String - this seems to be unnecessary. The error might be caused on how convertBase64() was called. For async methods like Future<T>, I suggest calling it like:
convertBase64(imageFile).then((String base64Image) {
// Handle base64Image
});
Also, as previously recommended in the comments, it's better to use Uri.dataFromBytes() instead of parsing the encoded String on your own.
Future<String> convertBase64(File file) async{
List<int> imageBytes = file.readAsBytesSync();
return Uri.dataFromBytes(imageBytes, mimeType: "image/png").toString();
}

Copy asset from bundle to file system

I'm trying to copy an sqlite database from the root bundle onto the file system in order to use it.
I've tried many different ways but it always ended up writing incorrect amounts of data to disk. The code I'm using looks like this:
Directory appDocDir = await getExternalStorageDirectory();
String path = join(appDocDir.path, "data.db");
bool exists = await new File(path).exists();
if (!exists) {
var out = new File(path).openWrite();
var data = await rootBundle.load("assets/data.sqlite");
var list = data.buffer.asUint8List();
out.write(list);
out.close();
}
I was able to do that by the followings.
ByteData data = await rootBundle.load("data.sqlite");
List<int> bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
Directory appDocDir = await getApplicationDocumentsDirectory()
String path = join(appDocDir.path, "data.db");
await File(path).writeAsBytes(bytes);
You can't write the contents of a Uint8List to a File using IOSink.write (which is designed to operate on String arguments). You'll end up writing the UTF-8 encoded representation of the String obtained by calling toString() on your Uint8List, which is probably much larger than the actual contents of the list.
Instead, you can write a Uint8List to the file using IOSink.add.
In the example you've provided, you could also write the entire file at once using new File(path).writeAsBytes(list).

How to convert a Dart Html client web socket response from Blob to Uint8List?

I’ve implemented my own binary message protocol for simple request/response objects from a Dart client to a Java server. These are encoded in Dart as an Uint8List and on the remote server in Java as a ByteBuffer. The round trip works for the WebSocket in [dart:io] because the websocket.listen stream handler in the Dart client command app is passed data typed as Uint8List.
In [dart:html] the response data in MessageEvent.data received from the websocket.onMessage stream is typed as Blob. I’m not finding a way to convert the Blob to Uint8List. Because the response will often be a large binary array of numbers (double) that will be supplying data to a virtual scrolling context, I want to minimize copying. Could someone please point me in the right direction.
According to this article, you need to use a FileReader to do this.
This example seems to work, the result type is a Uint8List when I tested this in Chrome.
var blob = new Blob(['abc']);
var r = new FileReader();
r.readAsArrayBuffer(blob);
r.onLoadEnd.listen((e) {
var data = r.result;
print(data.runtimeType);
});
Another option is to set WebSocket.binaryType to "arraybuffer". Then MessageEvent.data will return a ByteBuffer which can be turned into a Uint8List. See the example below.
import 'dart:html';
import 'dart:typed_data';
void main() {
var ws = new WebSocket('...')..binaryType = 'arraybuffer';
ws.onMessage.listen((MessageEvent e) {
ByteBuffer buf = e.data;
var data = buf.asUint8List();
// ...
});
}

In C#, how can I know the file type from a byte[]?

I have a byte array filled from a file uploaded. But, in another part of the code, I need to know this file type uploaded from the byte[] so I can render the correct content-type to browser!
Thanks!!
As mentioned, MIME magic is the only way to do this. Many platforms provide up-to-date and robust MIME magic files and code to do this efficiently. The only way to do this in .NET without any 3rd party code is to use FindMimeFromData from urlmon.dll. Here's how:
public static int MimeSampleSize = 256;
public static string DefaultMimeType = "application/octet-stream";
[DllImport(#"urlmon.dll", CharSet = CharSet.Auto)]
private extern static uint FindMimeFromData(
uint pBC,
[MarshalAs(UnmanagedType.LPStr)] string pwzUrl,
[MarshalAs(UnmanagedType.LPArray)] byte[] pBuffer,
uint cbSize,
[MarshalAs(UnmanagedType.LPStr)] string pwzMimeProposed,
uint dwMimeFlags,
out uint ppwzMimeOut,
uint dwReserverd
);
public static string GetMimeFromBytes(byte[] data) {
try {
uint mimeType;
FindMimeFromData(0, null, data, (uint)MimeSampleSize, null, 0, out mimeType, 0);
var mimePointer = new IntPtr(mimeType);
var mime = Marshal.PtrToStringUni(mimePointer);
Marshal.FreeCoTaskMem(mimePointer);
return mime ?? DefaultMimeType;
}
catch {
return DefaultMimeType;
}
}
This uses the Internet Explorer MIME detector. This is the same code used by IE to send a MIME type along with uploaded files. You can see the list of MIME types supported by urlmon.dll. One thing to watch out for is image/pjpeg and image/x-png which are non-standard. In my code I replace these with image/jpeg and image/png.
Not sure, but maybe you should investigate about magic numbers.
Update:
Reading about it, I don't think it's very reliable though.
If you know it's a System.Drawing.Image, you can do:
public static string GetMimeTypeFromImageByteArray(byte[] byteArray)
{
using (MemoryStream stream = new MemoryStream(byteArray))
using (Image image = Image.FromStream(stream))
{
return ImageCodecInfo.GetImageEncoders().First(codec => codec.FormatID == image.RawFormat.Guid).MimeType;
}
}
You can't know it from the byte stream, but you can store the MIME type when you initially populate the byte[].
Short answer: you can't
Longer answer: Usually, programs use the file extension to know what type of file they're dealing with. If you don't have that extension, you can only make guesses... for instance, you could look at the first few bytes and check if you recognize a well-known header (XML declaration tag for instance, or bitmap or JPEG header). But that will always be a guess in the end : without some metadata or information about the content, an array of bytes is just meaningless...
If you know extension of the file name, may be System.Web.MimeMapping will do the trick:
MimeMapping.GetMimeMapping(fileDisplayNameWithExtension)
I used it in MVC Action like this:
return File(fileDataByteArray, MimeMapping.GetMimeMapping(fileDisplayNameWithExtension), fileDisplayNameWithExtension);
Reminds me of back in the day we, er um "some people" used to share 50MB rar files on the early free image hosting sites, by just adding the .gif extension to the .rar filename.
Clearly if you are public facing and your are expecting a certain file type, and you have to be sure it is that file type, then you can't just trust the extension.
On the other hand, if your app would have no reason to distrust the the uploaded extension and or MIME type, then just get those when the file is uploaded like the answers you received from #rossfabircant and #RandolphPotter. create a type that has the byte[], as well as the original extension or mimetype, and pass that around.
If you need to verify that the file is actually a certain expected type like a valid .jpeg, or .png you can try to interpret the file as those types and see if it opens successfully. (System.Drawing.Imaging.ImageFormat)
If you are trying to classify the file only from the binary contents, and it could be any format in the whole wide world, that is really a tough, open-ended problem and there is no 100% reliable way to do it. You could invoke TrID against it, and there are likely similar forensics tools used by law enforcement investigators if you can find (and afford) them.
If you don't have to do it the hard way, don't.
You don't want to do it that way. Call Path.GetExtension when the file is uploaded, and pass the extension around with the byte[].
If you have a limited number of expected file types you want to support, magic numbers can be the way to go.
A simple way to check is to just open example files with a text/hex editor, and study the leading bytes to see if there is something there you can use to differentiate/discard files from the supported set.
If, on the other hand, you are looking to recognize any arbitrary file type, yeah, as everyone has stated already, tough.
Using the System.Drawing.Image 'RawFormat.Guid' Property you can detect MIME Type of Images.
but i am not sure how to find other File Types.
http://www.java2s.com/Code/CSharp/Network/GetImageMimeType.htm
UPDATE: you may try taking a look on this post
Using .NET, how can you find the mime type of a file based on the file signature not the extension
I got AccessViolationException while accessing memory using other answers, so I solved my problem using this code:
[DllImport("urlmon.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = false)]
private static extern int FindMimeFromData(IntPtr pBc,
[MarshalAs(UnmanagedType.LPWStr)] string pwzUrl,
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I1, SizeParamIndex = 3)]
byte[] pBuffer,
int cbSize,
[MarshalAs(UnmanagedType.LPWStr)] string pwzMimeProposed,
int dwMimeFlags,
out IntPtr ppwzMimeOut,
int dwReserved
);
/**
* This function will detect mime type from provided byte array
* and if it fails, it will return default mime type
*/
private static string GetMimeFromBytes(byte[] dataBytes, string defaultMimeType)
{
if (dataBytes == null) throw new ArgumentNullException(nameof(dataBytes));
var mimeType = string.Empty;
IntPtr suggestPtr = IntPtr.Zero, filePtr = IntPtr.Zero;
try
{
var ret = FindMimeFromData(IntPtr.Zero, null, dataBytes, dataBytes.Length, null, 0, out var outPtr, 0);
if (ret == 0 && outPtr != IntPtr.Zero)
{
mimeType = Marshal.PtrToStringUni(outPtr);
Marshal.FreeCoTaskMem(outPtr);
}
}
catch
{
mimeType = defaultMimeType;
}
return mimeType;
}
How to call it:
string ContentType = GetMimeFromBytes(byteArray, "image/jpeg");
Hope this helps!

ASP.NET MVC FileStreamResult not working as intended

I have the following code which I stripped out of any non-essential lines to leave the minimun reproducable case. What I expect is for it to return the image, but it doesn't. As far as I can see it returns an empty file:
public ActionResult Thumbnail(int id) {
var question = GetQuestion(db, id);
var image = new Bitmap(question.ImageFullPath);
MemoryStream stream = new MemoryStream();
image.Save(stream, ImageFormat.Jpeg);
return new FileStreamResult(stream, "image/jpeg");
}
Can you identify what's wrong with this code? In the debugger I can see that the stream grows in size so it seems to be getting the data although I haven't been able to verify it's the correct data. I have no idea how to debug the FileStreamResult itself.
You need to insert
stream.Seek(0, SeekOrigin.Begin);
after the call to
Image.Save()
This will rewind the stream to the beginning of the saved image. Otherwise the stream will be positioned at the end of the stream and nothing is sent to the receiver.
Try rewinding the MemoryStream. The "cursor" is left at the end of the file and there is nothing to read until you "rewind" the stream to the beginning.
image.Save( stream, ImageFormat.Jpeg );
stream.Seek( 0, SeekOrigin.Begin );
return new FileStreamResult( stream, "image/jpeg" );

Resources