How to get ByteData from a File - dart

I want to convert a File to a ByteData object in flutter.
Something like this:
import 'dart:io';
File file = getSomeCorrectFile(); //This file is correct
ByteData bytes = ByteData(file.readAsBytesSync()); //Doesnt compile
return bytes;
I understood that ByteData constructor receives the length of the amount of bytes and initialize them with 0, so I could do something like ByteData(file.readAsBytesStync().length); but then how do I fill them?
What am I missing?

In Dart 2.5.0 or later, I believe that the following should work:
import 'dart:io';
import 'dart:typed_data';
...
File file = getSomeCorrectFile();
Uint8List bytes = file.readAsBytesSync();
return ByteData.view(bytes.buffer);
(Prior to Dart 2.5.0, the file.readAsBytesSync() line should be:
Uint8List bytes = file.readAsBytesSync() as Uint8List;
File.readAsBytes/File.readAsBytesSync used to be declared to return a List<int>, but the returned object was actually a Uint8List subtype.)
Once you have the bytes as a Uint8List, you can extract its ByteBuffer and construct a ByteData from that.

in Dart 2.9:
import 'dart:io';
import 'dart:typed_data';
final file = getSomeCorrectFile(); // File
final bytes = await file.readAsBytes(); // Uint8List
final byteData = bytes.buffer.asByteData(); // ByteData
return byteData;

this is worked for me ...
Uint8List uint8list = Uint8List.fromList(File(path).readAsBytesSync())

Try this:
File file = getSomeCorrectFile();
ByteData bytes = await file.readAsBytes().then((data) => ByteData.view(data as ByteBuffer));
return bytes;

here is my class to handle this:
import 'dart:io';
import 'dart:typed_data';
class FileHandler {
final String _filePath;
FileHandler(this._filePath);
Future<Uint8List> _readToBytes() async {
var file = File.fromUri(Uri.parse(_filePath));
return await file.readAsBytes();
}
Future<Map<String, dynamic>> get data async {
var byte = await _readToBytes();
var ext = _filePath.split('.').last;
return {'byte': byte, 'extension': ext};
}
}

here is my class to handle this:
import 'dart:io';
import 'dart:typed_data';
class FileHandler {
final String _filePath;
FileHandler(this._filePath);
Future<Uint8List> _readToBytes() async {
var file = File.fromUri(Uri.parse(_filePath));
return await file.readAsBytes();
}
Future<Map<String, dynamic>> get data async {
var byte = await _readToBytes();
var ext = _filePath.split('.').last;
return {'byte': byte, 'extension': ext};
}
}

Related

X25519 calculated shared key is different in two programs

I have written two programs which will exchange X25519 public keys and then calculate shared secret. This is just a test, so public keys are exchanged trough text files (in PEM format), program is until you tell it to continue in order to generate keypairs and exchange them.
First one is in Dart:
import 'dart:convert';
import 'dart:io';
import 'package:cryptography/cryptography.dart';
import 'package:pem/pem.dart';
Future<void> main(List<String> arguments) async {
//https://pub.dev/documentation/cryptography/latest/cryptography/X25519-class.html
final algorithm = Cryptography.instance.x25519();
final keyPair = await algorithm.newKeyPair();
var file = File('/home/razj/dartPublic.txt');
file.writeAsStringSync(PemCodec(PemLabel.publicKey)
.encode(await keyPair.extractPublicKeyBytes()));
print('PRESS AFTER C# PROGRAM GENERATES KEYPAIR');
stdin.readLineSync();
String remotePem = File('/home/razj/sharpPublic.txt').readAsStringSync();
SimplePublicKey remotePublicKey = SimplePublicKey(
PemCodec(PemLabel.publicKey).decode(remotePem),
type: KeyPairType.x25519);
final sharedSecretKey = await algorithm.sharedSecretKey(
keyPair: keyPair,
remotePublicKey: remotePublicKey,
);
List<int> sharedKeyBytes = await sharedSecretKey.extractBytes();
print(base64.encode(sharedKeyBytes));
}
extension SimpleKeyPairExtension on SimpleKeyPair {
Future<List<int>> extractPublicKeyBytes() {
return extract().then((value) => value.bytes);
}
}
Second is in .NET CORE:
using System.Security.Cryptography;
using X25519;
internal class Program
{
private static void Main(string[] args)
{
//https://github.com/HirbodBehnam/X25519-CSharp
var keyPair = X25519KeyAgreement.GenerateKeyPair();
File.WriteAllText("/home/razj/sharpPublic.txt", new string(PemEncoding.Write("PUBLIC KEY", keyPair.PublicKey)));
Console.WriteLine("PRESS AFTER DART PROGRAM GENERATES KEYPAIR");
Console.ReadKey(true);
string remotePem = File.ReadAllText("/home/razj/dartPublic.txt").Replace("-----BEGIN PUBLIC KEY-----\n", "").Replace("\n-----END PUBLIC KEY-----\n", "");
byte[] sharedKeyBytes = X25519KeyAgreement.Agreement(keyPair.PrivateKey, Convert.FromBase64String(remotePem));
Console.WriteLine(Convert.ToBase64String(sharedKeyBytes));
}
}
In the end of each program I print base64 encoded byte array which represents the shared key. Unfortunately outputs doesn't match. Any idea how to fix this or what might be wrong? Thanks for answering.
As #Topaco said,
However, you simply apply the raw 32 bytes key instead of the DER
encoded key, so the PEM key is invalid. This has no consequence, since
the reverse direction returns the raw keys that both libraries use in
the end. It is unnecessary though, just apply the (Base64 encoded) raw
keys directly.
Just exchanged keys by encoding raw key bytes to Base64 string:
Dart:
import 'dart:io';
import 'dart:convert';
import 'package:cryptography/cryptography.dart';
Future<void> main(List<String> arguments) async {
//https://pub.dev/documentation/cryptography/latest/cryptography/X25519-class.html
final algorithm = Cryptography.instance.x25519();
final keyPair = await algorithm.newKeyPair();
var file = File('/home/razj/dartPublic.txt');
SimplePublicKey publicKey = await keyPair.extractPublicKey();
//saving key bytes as base64 string
file.writeAsStringSync(base64.encode(publicKey.bytes));
print('PRESS AFTER C# PROGRAM GENERATES KEYPAIR');
stdin.readLineSync();
String remoteKeyBase64 =
File('/home/razj/sharpPublic.txt').readAsStringSync();
SimplePublicKey remotePublicKey =
SimplePublicKey(base64.decode(remoteKeyBase64), type: KeyPairType.x25519);
final sharedSecretKey = await algorithm.sharedSecretKey(
keyPair: keyPair,
remotePublicKey: remotePublicKey,
);
List<int> sharedKeyBytes = await sharedSecretKey.extractBytes();
print(base64.encode(sharedKeyBytes));
}
.NET CORE
using X25519;
internal class Program
{
private static void Main(string[] args)
{
//https://github.com/HirbodBehnam/X25519-CSharp
var keyPair = X25519KeyAgreement.GenerateKeyPair();
//saving key bytes as base64 string
File.WriteAllText("/home/razj/sharpPublic.txt", Convert.ToBase64String(keyPair.PublicKey));
Console.WriteLine("PRESS AFTER DART PROGRAM GENERATES KEYPAIR");
Console.ReadKey(true);
string remoteKeyBase64 = File.ReadAllText("/home/razj/dartPublic.txt");
byte[] sharedKeyBytes = X25519KeyAgreement.Agreement(keyPair.PrivateKey, Convert.FromBase64String(remoteKeyBase64));
Console.WriteLine(Convert.ToBase64String(sharedKeyBytes));
}
}
Dart output:
Hz2Rf2nUFbwmg4wgaXOl3qAi8ha5h61fHcMOXpNQ23o=
.NET-CORE output
Hz2Rf2nUFbwmg4wgaXOl3qAi8ha5h61fHcMOXpNQ23o=

Dart read json objects from Uneditable large file

i have a large file which that uneditable and i have more jsonObject into that, for example:
{"id":"#123456","v":"1"}
this isn't jsonArray which i can't parse and read it, for example:
String file= new File('file.json').readAsStringSync();
Map<String, dynamic> data = json.decode(file);
you think how can i read this objects from file and show key and values?
Try the following solution. I ended up making a parser for your input format which are returning each parsed JSON object in a Stream.
The parser will not work if any strings in your JSON contains { or }. If that is the case, I can expand the parser so it takes this into account but I don't want to make it more advanced than necessary.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
Future<void> main() async {
final ids = await File('large_file.json')
.openRead()
.transform(const Utf8Decoder())
.transform<dynamic>(JsonObjectTransformer())
.map((dynamic json) => json['id'] as String)
.toList();
print(ids); // [#123456, #123456]
}
class JsonObjectTransformer extends StreamTransformerBase<String, dynamic> {
static final _openingBracketChar = '{'.codeUnitAt(0);
static final _closingBracketChar = '}'.codeUnitAt(0);
#override
Stream<dynamic> bind(Stream<String> stream) async* {
final sb = StringBuffer();
var bracketsCount = 0;
await for (final string in stream) {
for (var i = 0; i < string.length; i++) {
final current = string.codeUnitAt(i);
sb.writeCharCode(current);
if (current == _openingBracketChar) {
bracketsCount++;
}
if (current == _closingBracketChar && --bracketsCount == 0) {
yield json.decode(sb.toString());
sb.clear();
}
}
}
}
}

How to transform a Sink<T> into a Sink<V>?

I have a method that can emit its output into a given Sink<Node>.
I wanted to pipe that into stdout which is a Sink<List<int>>.
Supposing I have a function convert that converts Node to List<int>, how can I transform stdout into a Sink<Node>, so that it will print my Tree to the console?
I have made this example showing how you can do it with a StreamController:
import 'dart:async';
import 'dart:convert';
import 'dart:io';
class Message {
String text;
Message(this.text);
}
void main() {
final controller = StreamController<Message>();
stdout.addStream(controller.stream
.map((var msg) => msg.text)
.transform(const Utf8Encoder()));
var messageSink = controller.sink;
messageSink.add(Message('Hello World'));
}
The StreamController in this example takes Message objects and converts them into List<int> by first using map to convert the Message to String object and then use a transformer to convert the String into a List of UTF8 bytes.
I've filed: https://github.com/dart-lang/sdk/issues/50607
Here is how I solved this:
class _MappedSink<From, To> implements Sink<From> {
final To Function(From) _transform;
final Sink<To> _sink;
const _MappedSink(this._sink, this._transform);
#override
void add(From data) => _sink.add(_transform(data));
#override
void close() => _sink.close();
}
extension SinkMap<To> on Sink<To> {
Sink<From> map<From>(To Function(From) transform) =>
_MappedSink(this, transform);
}

How can I write uploaded multipart files to disk?

I'm trying to handle file upload through multipart requests with Aqueduct. Aqueduct has now an example on how to handle multipart requests here:
https://aqueduct.io/docs/http/request_and_response/#example-multipartform-data
The example explains, how to get the header and the content of the files. However it doesn't explain how to write the content into a file on disk.
How can I write the content of the files uploaded to disk?
Below an example that shows what I want to achieve, but doesn't work:
import 'dart:io';
import 'package:aqueduct/aqueduct.dart';
import 'package:mime/mime.dart';
class MediaUploadController extends ResourceController {
MediaUploadController() {
acceptedContentTypes = [ContentType("multipart", "form-data")];
}
#Operation.post()
Future<Response> postMultipartForm() async {
final transformer = MimeMultipartTransformer(request.raw.headers.contentType.parameters["boundary"]);
final bodyStream = Stream.fromIterable([await request.body.decode<List<int>>()]);
final parts = await transformer.bind(bodyStream).toList();
for (var part in parts) {
final String contentType = part.headers["content-type"];
// Write content to disk
final content = await part.toList();
final fileName = DateTime.now().millisecondsSinceEpoch.toString() + ".jpg";
var file = new File('data/' + fileName);
var sink = file.openWrite();
sink.write(content);
sink.close();
}
return new Response.ok({});
}
}
This below actually worked. Additionally to the mime package, I have also added the http_server package to pubspec.yaml, because it makes it easier to handle the multipart form data.
dependencies:
aqueduct: ^3.0.1
mime: ^0.9.6+2
http_server: ^0.9.8+1
Then I studied some other frameworks to see how they handled writing to the file. It's so complicated to get how this multipart stuff and streams work together. But at last after nearly a week, the light at the end of the tunnel... until the next questions pop up. Most likely 10 minutes down the line :)
import 'dart:io';
import 'package:aqueduct/aqueduct.dart';
import 'package:mime/mime.dart';
import 'package:http_server/http_server.dart';
class MediaUploadController extends ResourceController {
MediaUploadController() {
acceptedContentTypes = [ContentType("multipart", "form-data")];
}
#Operation.post()
Future<Response> postMultipartForm() async {
final transformer = MimeMultipartTransformer(request.raw.headers.contentType.parameters["boundary"]);
final bodyStream = Stream.fromIterable([await request.body.decode<List<int>>()]);
final parts = await transformer.bind(bodyStream).toList();
for (var part in parts) {
HttpMultipartFormData multipart = HttpMultipartFormData.parse(part);
final ContentType contentType = multipart.contentType;
final content = multipart.cast<List<int>>();
final filePath = "data/" + DateTime.now().millisecondsSinceEpoch.toString() + ".jpg";
IOSink sink = File(filePath).openWrite();
await for (List<int> item in content) {
sink.add(item);
}
await sink.flush();
await sink.close();
}
return new Response.ok({});
}
}

How to write a `ByteData` instance to a File in Dart?

I am using Flutter to load an "asset" into a File so that a native application can access it.
This is how I load the asset:
final dbBytes = await rootBundle.load('assets/file');
This returns an instance of ByteData.
How can I write this to a dart.io.File instance?
ByteData is an abstraction for:
A fixed-length, random-access sequence of bytes that also provides
random and unaligned access to the fixed-width integers and floating
point numbers represented by those bytes.
As Gunter mentioned in the comments, you can use File.writeAsBytes. It does require a bit of API work to get from ByteData to a List<int>, however.
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
Future<void> writeToFile(ByteData data, String path) {
final buffer = data.buffer;
return new File(path).writeAsBytes(
buffer.asUint8List(data.offsetInBytes, data.lengthInBytes));
}
I've also filed an issue to make the docs on Flutter more clear for this use case.
you need to have path_provider package installed, then
This should work :
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:path_provider/path_provider.dart';
final dbBytes = await rootBundle.load('assets/file'); // <= your ByteData
//=======================
Future<File> writeToFile(ByteData data) async {
final buffer = data.buffer;
Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;
var filePath = tempPath + '/file_01.tmp'; // file_01.tmp is dump file, can be anything
return new File(filePath).writeAsBytes(
buffer.asUint8List(data.offsetInBytes, data.lengthInBytes));
}
//======================
to get your file :
var file;
try {
file = await writeToFile(dbBytes); // <= returns File
} catch(e) {
// catch errors here
}
Hope this helps,
Thank you.
to search flutter ByteData to List<int> then found here, but not fully answer my question:
how to convert ByteData to List<int> ?
after self investigate, solution is:
use .cast<int>()
ByteData audioByteData = await rootBundle.load(audioAssetsFullPath);
Uint8List audioUint8List = audioByteData.buffer.asUint8List(audioByteData.offsetInBytes, audioByteData.lengthInBytes);
List<int> audioListInt = audioUint8List.cast<int>();
or 2. use .map
ByteData audioByteData = await rootBundle.load(audioAssetsFullPath);
Uint8List audioUint8List = audioByteData.buffer.asUint8List(audioByteData.offsetInBytes, audioByteData.lengthInBytes);
List<int> audioListInt = audioUint8List.map((eachUint8) => eachUint8.toInt()).toList();
For those looking to write bytes (aka Uint8List) instead of ByteData please note that ByteData is a wrapper for Uint8List.
From /runtime/lib/typed_data.patch:
#patch
class ByteData implements TypedData {
#patch
#pragma("vm:entry-point")
factory ByteData(int length) {
final list = new Uint8List(length) as _TypedList;
_rangeCheck(list.lengthInBytes, 0, length);
return new _ByteDataView(list, 0, length);
}
#patch
class Uint8List {
#patch
#pragma("vm:exact-result-type", _Uint8List)
factory Uint8List(int length) native "TypedData_Uint8Array_new";
}
If you are using the latter type you can use the answer provided by Rami and modify the return as follow:
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:path_provider/path_provider.dart';
Future<File> writeToFile(Uint8List data) async {
(...)
return new File(filePath).writeAsBytes(data);
}

Resources