59

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?

Renato
  • 12,940
  • 3
  • 54
  • 85

4 Answers4

82

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.

lrn
  • 64,680
  • 7
  • 105
  • 121
matanlurey
  • 8,096
  • 3
  • 38
  • 46
  • 2
    Ah, cool. I ended up doing `destinationFile.writeAsBytes(dbBytes.buffer.asUint8List());` (in the debugger, I saw that `dbBytes` contained a `Uint8List`). I guess that's the same as you suggested, or is one more efficient than the other? – Renato May 01 '18 at 18:31
  • Remember that a `ByteData` might be a view on part of a buffer, so you might not want to write the entire buffer. I've updated the example to account for that. – lrn May 02 '18 at 09:00
  • For some reason, your `writeToFile` does not work for >665MB of data. Do you know if I have to set gradle.properties or similar ? Or do you have another suggestion on how to write large Files ? – iKK Jan 12 '19 at 14:21
  • 2
    @matanlurey What do you give as path? – Ant D Mar 23 '19 at 15:01
24

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.

Rami Mohamed
  • 2,505
  • 3
  • 25
  • 33
20

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:

  1. 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();
crifan
  • 12,947
  • 1
  • 71
  • 56
7

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);
}
Antonin GAVREL
  • 9,682
  • 8
  • 54
  • 81