How to write a `ByteData` instance to a File in Dart? - 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);
}

Related

SQFLITE ERROR: SqfliteDatabaseException (DatabaseException(database_closed))

The two pages in the application are listed with database(sqlite). but when I want to switch between pages, I get such an error: SqfliteDatabaseException (DatabaseException(database_closed))
please help mee.. I don't understand why see this error.
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:bankingapp/models/coin.dart';
import 'package:bankingapp/models/histories.dart';
import 'package:flutter/services.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DbHelper {
static Database? _db;
Future<Database> get db async {
return _db ??= await initDb();
}
Future<Database> initDb() async {
var dbFolder = await getDatabasesPath();
String path = join(dbFolder, 'app.db');
// Delete any existing database:
await deleteDatabase(path);
// Create the writable database file from the bundled demo database file:
try {
await Directory(dirname(path)).create(recursive: true);
} catch (_) {}
ByteData data =
await rootBundle.load(join("assets/database", "bankingapp.db"));
List<int> bytes =
data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
await new File(path).writeAsBytes(bytes, flush: true);
//open the database
return await openDatabase(path);
}
Future<List<Histories>> getHistories() async {
var dbClient = await db;
var result = await dbClient.rawQuery("SELECT * FROM Histories");
return result.map((data) => Histories.fromMap(data)).toList();
}
Future<List<Coins>> getCoins() async {
var dbClient = await db;
var result = await dbClient.rawQuery("SELECT * FROM Coins");
return result.map((data) => Coins.fromMap(data)).toList();
}
}
I don't exactly know the answer but I suggest you to use Floor package. This is really simple to implement and it is the abstraction of SQLite database so I think it will be familiar to you. I don't seem to face such error with this package.
https://pub.dev/packages/floor

How to get ByteData from a File

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};
}
}

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({});
}
}

Generate one file for a list of parsed files using source_gen in dart

I have a list of models that I need to create a mini reflective system.
I analyzed the Serializable package and understood how to create one generated file per file, however, I couldn't find how can I create one file for a bulk of files.
So, how to dynamically generate one file, using source_gen, for a list of files?
Example:
Files
user.dart
category.dart
Generated:
info.dart (containg information from user.dart and category.dart)
Found out how to do it with the help of people in Gitter.
You must have one file, even if empty, to call the generator. In my example, it is lib/batch.dart.
source_gen: ^0.5.8
Here is the working code:
The tool/build.dart
import 'package:build_runner/build_runner.dart';
import 'package:raoni_global/phase.dart';
main() async {
PhaseGroup pg = new PhaseGroup()
..addPhase(batchModelablePhase(const ['lib/batch.dart']));
await build(pg,
deleteFilesByDefault: true);
}
The phase:
batchModelablePhase([Iterable<String> globs =
const ['bin/**.dart', 'web/**.dart', 'lib/**.dart']]) {
return new Phase()
..addAction(
new GeneratorBuilder(const
[const BatchGenerator()], isStandalone: true
),
new InputSet(new PackageGraph.forThisPackage().root.name, globs));
}
The generator:
import 'dart:async';
import 'package:analyzer/dart/element/element.dart';
import 'package:build/build.dart';
import 'package:source_gen/source_gen.dart';
import 'package:glob/glob.dart';
import 'package:build_runner/build_runner.dart';
class BatchGenerator extends Generator {
final String path;
const BatchGenerator({this.path: 'lib/models/*.dart'});
#override
Future<String> generate(Element element, BuildStep buildStep) async {
// this makes sure we parse one time only
if (element is! LibraryElement)
return null;
String libraryName = 'raoni_global', filePath = 'lib/src/model.dart';
String className = 'Modelable';
// find the files at the path designed
var l = buildStep.findAssets(new Glob(path));
// get the type of annotation that we will use to search classes
var resolver = await buildStep.resolver;
var assetWithAnnotationClass = new AssetId(libraryName, filePath);
var annotationLibrary = resolver.getLibrary(assetWithAnnotationClass);
var exposed = annotationLibrary.getType(className).type;
// the caller library' name
String libName = new PackageGraph.forThisPackage().root.name;
await Future.forEach(l.toList(), (AssetId aid) async {
LibraryElement lib;
try {
lib = resolver.getLibrary(aid);
} catch (e) {}
if (lib != null && Utils.isNotEmpty(lib.name)) {
// all objects within the file
lib.units.forEach((CompilationUnitElement unit) {
// only the types, not methods
unit.types.forEach((ClassElement el) {
// only the ones annotated
if (el.metadata.any((ElementAnnotation ea) =>
ea.computeConstantValue().type == exposed)) {
// use it
}
});
});
}
});
return '''
$libName
''';
}
}
It seems what you want is what this issue is about How to generate one output from many inputs (aggregate builder)?
[Günter]'s answer helped me somewhat.
Buried in that thread is another thread which links to a good example of an aggregating builder:
1https://github.com/matanlurey/build/blob/147083da9b6a6c70c46eb910a3e046239a2a0a6e/docs/writing_an_aggregate_builder.md
The gist is this:
import 'package:build/build.dart';
import 'package:glob/glob.dart';
class AggregatingBuilder implements Builder {
/// Glob of all input files
static final inputFiles = new Glob('lib/**');
#override
Map<String, List<String>> get buildExtensions {
/// '$lib$' is a synthetic input that is used to
/// force the builder to build only once.
return const {'\$lib$': const ['all_files.txt']};
}
#override
Future<void> build(BuildStep buildStep) async {
/// Do some operation on the files
final files = <String>[];
await for (final input in buildStep.findAssets(inputFiles)) {
files.add(input.path);
}
String fileContent = files.join('\n');
/// Write to the file
final outputFile = AssetId(buildStep.inputId.package,'lib/all_files.txt');
return buildStep.writeAsString(outputFile, fileContent);
}
}

How do I read console input / stdin in Dart?

How do I read console input from stdin in Dart?
Is there a scanf in Dart?
The readLineSync() method of stdin allows to capture a String from the console:
import 'dart:convert';
import 'dart:io';
void main() {
print('1 + 1 = ...');
var line = stdin.readLineSync(encoding: utf8);
print(line?.trim() == '2' ? 'Yup!' : 'Nope :(');
}
Old version:
import 'dart:io';
main() {
print('1 + 1 = ...');
var line = stdin.readLineSync(encoding: Encoding.getByName('utf-8'));
print(line.trim() == '2' ? 'Yup!' : 'Nope :(');
}
The following should be the most up to date dart code to read input from stdin.
import 'dart:async';
import 'dart:io';
import 'dart:convert';
void main() {
readLine().listen(processLine);
}
Stream<String> readLine() => stdin
.transform(utf8.decoder)
.transform(const LineSplitter());
void processLine(String line) {
print(line);
}
import 'dart:io';
void main(){
stdout.write("Enter your name : ");
var name = stdin.readLineSync();
stdout.write(name);
}
Output
Enter your name : Jay
Jay
By default readLineSync() takes input as string. But If you want integer input then you have to use parse() or tryparse().
With M3 dart classes like StringInputStream are replaced with Stream, try this:
import 'dart:io';
import 'dart:async';
void main() {
print("Please, enter a line \n");
Stream cmdLine = stdin
.transform(new StringDecoder())
.transform(new LineTransformer());
StreamSubscription cmdSubscription = cmdLine.listen(
(line) => print('Entered line: $line '),
onDone: () => print(' finished'),
onError: (e) => /* Error on input. */);
}
As of Dart 2.12, null-safety is enabled, and stdin.readLineSync() now returns a String? instead of a String.
This apparently has been confusing a lot of people. I highly recommend reading https://dart.dev/null-safety/understanding-null-safety to understand what null-safety means.
For stdin.readLineSync() specifically, you can resolve this by checking for null first, which for local variables will automatically promote a String? to a String. Here are some examples:
// Read a line and try to parse it as an integer.
String? line = stdin.readLineSync();
if (line != null) {
int? num = int.tryParse(line); // No more error about `String?`.
if (num != null) {
// Do something with `num`...
}
}
// Read lines from `stdin` until EOF is reached, storing them in a `List<String>`.
var lines = <String>[];
while (true) {
var line = stdin.readLineSync();
if (line == null) {
break;
}
lines.add(line); // No more error about `String?`.
}
// Read a line. If EOF is reached, treat it as an empty string.
String line = stdin.readLineSync() ?? '';
Note that you should not blindly do stdin.readLineSync()!. readLineSync returns a String? for a reason: it returns null when there is no more input. Using the null assertion operator (!) is asking for a runtime exception.
Note that while calling stdin.readLineSync() your isolate/thread will be blocked, no other Future will be completed.
If you want to read a stdin String line asynchronously, avoiding isolate/thread block, this is the way:
import 'dart:async';
import 'dart:convert';
import 'dart:io';
/// [stdin] as a broadcast [Stream] of lines.
Stream<String> _stdinLineStreamBroadcaster = stdin
.transform(utf8.decoder)
.transform(const LineSplitter()).asBroadcastStream() ;
/// Reads a single line from [stdin] asynchronously.
Future<String> _readStdinLine() async {
var lineCompleter = Completer<String>();
var listener = _stdinLineStreamBroadcaster.listen((line) {
if (!lineCompleter.isCompleted) {
lineCompleter.complete(line);
}
});
return lineCompleter.future.then((line) {
listener.cancel();
return line ;
});
}
All these async API readLine*() based solutions miss the syntactic sugar which gives you the ability to do everything without synchronous blocking, but written like synchronous code. This is even more intuitive coming from other languages where you write code to execute synchronously:
import 'dart:convert';
import 'dart:io';
Future<void> main() async {
var lines = stdin.transform(utf8.decoder).transform(const LineSplitter());
await for (final l in lines) {
print(l);
}
print("done");
}
The key takeaway here is to make use of async and await:
async on your method is required, as you're using await to interface with asynchronous API calls
await for is the syntax for doing "synchronous-like" code on a Stream (the corresponding syntax for a Future is just await).
Think of await like "unwrapping" a Stream/Future for you by making the following code execute once something is available to be handled. Now you're no longer blocking your main thread (Isolate) to do the work.
For more information, see the Dart codelab on async/await.
(Sidenote: The correct way to declare any return value for an async function is to wrap it in a Future, hence Future<void> here.)
You can use the following line to read a string from the user:
String str = stdin.readLineSync();
OR the following line to read a number
int n = int.parse(stdin.readLineSync());
Consider the following example:
import 'dart:io'; // we need this to use stdin
void main()
{
// reading the user name
print("Enter your name, please: ");
String name = stdin.readLineSync();
// reading the user age
print("Enter your age, please: ");
int age = int.parse(stdin.readLineSync());
// Printing the data
print("Hello, $name!, Your age is: $age");
/* OR print in this way
* stdout.write("Hello, $name!, Your age is: $age");
* */
}
You could of course just use the dcli package and it's ask function
Import 'package: dcli/dcli.dart':
Var answer = ask('enter your name');
print (name);
Use the named validator argument to restrict input to integers.
To read from the console or terminal in Dart, you need to:
import 'dart:io' library
store the entered value using stdin.readLineSync()!
parse the input into an int using int.parse(input) if necessary
Code:
import 'dart:io';
void main() {
String? string;
var number;
stdout.writeln("Enter a String: ");
string = stdin.readLineSync()!;
stdout.writeln("Enter a number: ");
number = int.parse(stdin.readLineSync()!);
}

Resources