|
@@ -1,12 +1,16 @@
|
|
|
import 'dart:typed_data';
|
|
|
|
|
|
+import 'package:dio/dio.dart';
|
|
|
+import 'package:flutter/foundation.dart';
|
|
|
+
|
|
|
//Raised when data is not ready.
|
|
|
class NotReadyException implements Exception {}
|
|
|
|
|
|
-class StreamErrorException implements Exception {
|
|
|
+//Raised when errro happened during the downloading.
|
|
|
+class DownloadErrorException implements Exception {
|
|
|
String _msg;
|
|
|
|
|
|
- StreamErrorException(this._msg);
|
|
|
+ DownloadErrorException(this._msg);
|
|
|
|
|
|
@override
|
|
|
String toString() {
|
|
@@ -14,40 +18,110 @@ class StreamErrorException implements Exception {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-class VidUsDataStreamReader {
|
|
|
+typedef DownloadCallback = void Function(double progress);
|
|
|
+
|
|
|
+class VidUsDataHttpReader {
|
|
|
late final List<int> _data = [];
|
|
|
late int _index;
|
|
|
late ByteBuffer _buffer;
|
|
|
late bool _error;
|
|
|
late String _errorMsg;
|
|
|
+ late String _url;
|
|
|
+ late int _chunkSize;
|
|
|
+ late Dio _dio;
|
|
|
|
|
|
- VidUsDataStreamReader(Stream<Uint8List> stream, [int offset = 0]) {
|
|
|
+ DownloadCallback? downloadCallback;
|
|
|
+
|
|
|
+ VidUsDataHttpReader(String url, {this.downloadCallback, int chunkSize = 32768}) {
|
|
|
+ _chunkSize = chunkSize;
|
|
|
+ _url = url;
|
|
|
_error = false;
|
|
|
_errorMsg = '';
|
|
|
_buffer = Uint8List.fromList(_data).buffer;
|
|
|
- stream.listen(onDataArrived, onError: onStreamError, onDone: onStreamDone);
|
|
|
- _index = offset;
|
|
|
+ _index = 0;
|
|
|
+ _dio = Dio();
|
|
|
+ //TODO let the user set the timeout.
|
|
|
+ _dio.options.connectTimeout = 5000; //5S
|
|
|
+ _dio.options.receiveTimeout = 3000;
|
|
|
+ _startDownload();
|
|
|
}
|
|
|
|
|
|
- void onDataArrived(Uint8List data) {
|
|
|
- _data.addAll(data);
|
|
|
- _buffer = Uint8List.fromList(_data).buffer;
|
|
|
+ void _startDownload() async {
|
|
|
+ //get file info
|
|
|
+ var fileSize = await _getFileSize();
|
|
|
+ if (!_error) {
|
|
|
+ var last = fileSize % _chunkSize;
|
|
|
+ var chunkCount = fileSize ~/ _chunkSize;
|
|
|
+ for (var i = 0; i < chunkCount; i++) {
|
|
|
+ var start = i * _chunkSize;
|
|
|
+ var end = start + _chunkSize;
|
|
|
+ await _downloadChunk(start, end);
|
|
|
+ if (_error) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (downloadCallback != null) {
|
|
|
+ var progress = _data.length / fileSize;
|
|
|
+ downloadCallback!(progress);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!_error && last > 0) {
|
|
|
+ var start = chunkCount * _chunkSize;
|
|
|
+ var end = start + last;
|
|
|
+ await _downloadChunk(start, end);
|
|
|
+ if (!_error && downloadCallback != null) {
|
|
|
+ var progress = _data.length / fileSize;
|
|
|
+ downloadCallback!(progress);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Future<int> _getFileSize() async {
|
|
|
+ var response = await _dio.head(_url);
|
|
|
+ if (response.statusCode != 200) {
|
|
|
+ _error = true;
|
|
|
+ _errorMsg = 'Get file info error.';
|
|
|
+ return 0;
|
|
|
+ } else {
|
|
|
+ var contentLength = response.headers.value(Headers.contentLengthHeader);
|
|
|
+ if (contentLength == null) {
|
|
|
+ _error = true;
|
|
|
+ _errorMsg = 'No Content-Length from headers.';
|
|
|
+ return 0;
|
|
|
+ } else {
|
|
|
+ var fileSize = int.parse(contentLength);
|
|
|
+ return fileSize;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- void onStreamError(msg) {
|
|
|
- _error = true;
|
|
|
- _errorMsg = msg.toString();
|
|
|
+ Future _downloadChunk(int chunkStart, int chunkEnd) async {
|
|
|
+ var response = await _dio.get<List<int>>(_url, options: Options(responseType: ResponseType.bytes, headers: {"range": "bytes=$chunkStart-$chunkEnd"}));
|
|
|
+ if (response.statusCode == 206) {
|
|
|
+ var downloadedData = response.data;
|
|
|
+ if (downloadedData == null) {
|
|
|
+ _error = true;
|
|
|
+ _errorMsg = 'Download chunk error.';
|
|
|
+ } else {
|
|
|
+ //Update the buffer.
|
|
|
+ _data.addAll(downloadedData);
|
|
|
+ _buffer = Uint8List.fromList(_data).buffer;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ _error = true;
|
|
|
+ _errorMsg = 'Target url does not support download with chunks.';
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- void onStreamDone() {
|
|
|
- //Update the last data into the buffer.
|
|
|
- _buffer = Uint8List.fromList(_data).buffer;
|
|
|
+ //Close the reader and its http connection.
|
|
|
+ void close(){
|
|
|
+ _dio.close(force: true);
|
|
|
}
|
|
|
|
|
|
///Read int value from binary data(little endian),
|
|
|
int readInt([int index = -1]) {
|
|
|
if (_error) {
|
|
|
- throw StreamErrorException(_errorMsg);
|
|
|
+ throw DownloadErrorException(_errorMsg);
|
|
|
}
|
|
|
if (index == -1) {
|
|
|
if ((_buffer.lengthInBytes - _index) < 4) {
|
|
@@ -69,7 +143,7 @@ class VidUsDataStreamReader {
|
|
|
///Read string from the binary data, the string format is utf-16, which is unicode in C#
|
|
|
String readString([int index = -1]) {
|
|
|
if (_error) {
|
|
|
- throw StreamErrorException(_errorMsg);
|
|
|
+ throw DownloadErrorException(_errorMsg);
|
|
|
}
|
|
|
int dataLength;
|
|
|
if (index == -1) {
|
|
@@ -92,7 +166,7 @@ class VidUsDataStreamReader {
|
|
|
///Read int16 value from binary data(little endian)
|
|
|
int readInt16([int index = -1]) {
|
|
|
if (_error) {
|
|
|
- throw StreamErrorException(_errorMsg);
|
|
|
+ throw DownloadErrorException(_errorMsg);
|
|
|
}
|
|
|
if (index == -1) {
|
|
|
if ((_buffer.lengthInBytes - _index) < 2) {
|
|
@@ -115,7 +189,7 @@ class VidUsDataStreamReader {
|
|
|
///this method is not support in web platform use readInt64V2 instead.
|
|
|
int readInt64([int index = -1]) {
|
|
|
if (_error) {
|
|
|
- throw StreamErrorException(_errorMsg);
|
|
|
+ throw DownloadErrorException(_errorMsg);
|
|
|
}
|
|
|
if (index == -1) {
|
|
|
if ((_buffer.lengthInBytes - _index) < 8) {
|
|
@@ -138,7 +212,7 @@ class VidUsDataStreamReader {
|
|
|
///this method use two int32 to support read int64 on web platform
|
|
|
int readInt64V2([int index = -1]) {
|
|
|
if (_error) {
|
|
|
- throw StreamErrorException(_errorMsg);
|
|
|
+ throw DownloadErrorException(_errorMsg);
|
|
|
}
|
|
|
if (index == -1) {
|
|
|
if ((_buffer.lengthInBytes - _index) < 8) {
|
|
@@ -165,7 +239,7 @@ class VidUsDataStreamReader {
|
|
|
///Read float value from binary data(little endian)
|
|
|
double readFloat([int index = -1]) {
|
|
|
if (_error) {
|
|
|
- throw StreamErrorException(_errorMsg);
|
|
|
+ throw DownloadErrorException(_errorMsg);
|
|
|
}
|
|
|
if (index == -1) {
|
|
|
if ((_buffer.lengthInBytes - _index) < 4) {
|
|
@@ -187,7 +261,7 @@ class VidUsDataStreamReader {
|
|
|
///Read double value from binary data(little endian)
|
|
|
double readDouble([int index = -1]) {
|
|
|
if (_error) {
|
|
|
- throw StreamErrorException(_errorMsg);
|
|
|
+ throw DownloadErrorException(_errorMsg);
|
|
|
}
|
|
|
if (index == -1) {
|
|
|
if ((_buffer.lengthInBytes - _index) < 8) {
|
|
@@ -209,7 +283,7 @@ class VidUsDataStreamReader {
|
|
|
///Read bool value from binary data
|
|
|
bool readBool([int index = -1]) {
|
|
|
if (_error) {
|
|
|
- throw StreamErrorException(_errorMsg);
|
|
|
+ throw DownloadErrorException(_errorMsg);
|
|
|
}
|
|
|
if (index == -1) {
|
|
|
if ((_buffer.lengthInBytes - _index) < 1) {
|
|
@@ -231,7 +305,7 @@ class VidUsDataStreamReader {
|
|
|
///Read byte value from binary data
|
|
|
int readByte([int index = -1]) {
|
|
|
if (_error) {
|
|
|
- throw StreamErrorException(_errorMsg);
|
|
|
+ throw DownloadErrorException(_errorMsg);
|
|
|
}
|
|
|
if (index == -1) {
|
|
|
if ((_buffer.lengthInBytes - _index) < 1) {
|
|
@@ -253,7 +327,7 @@ class VidUsDataStreamReader {
|
|
|
///Read bytes from the binary data.
|
|
|
Uint8List readBytes([int index = -1]) {
|
|
|
if (_error) {
|
|
|
- throw StreamErrorException(_errorMsg);
|
|
|
+ throw DownloadErrorException(_errorMsg);
|
|
|
}
|
|
|
var dataLength = readInt(index);
|
|
|
if ((_buffer.lengthInBytes - _index) < dataLength) {
|