Browse Source

Add support for http chunks

Justin 2 years ago
parent
commit
01b008cc70
3 changed files with 127 additions and 91 deletions
  1. 99 25
      lib/us/vid_us_data_http_reader.dart
  2. 26 27
      lib/us/vid_us_image_data.dart
  3. 2 39
      pubspec.yaml

+ 99 - 25
lib/us/vid_us_data_stream_reader.dart → lib/us/vid_us_data_http_reader.dart

@@ -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) {

+ 26 - 27
lib/us/vid_us_image_data.dart

@@ -1,6 +1,6 @@
 import 'dart:typed_data';
+import 'package:vid/us/vid_us_data_http_reader.dart';
 import 'package:vid/us/vid_us_data_reader.dart';
-import 'package:vid/us/vid_us_data_stream_reader.dart';
 import 'package:vid/us/vid_us_image.dart';
 import 'package:vid/us/vid_us_probe.dart';
 
@@ -78,8 +78,8 @@ class ReadTimeoutException implements Exception {}
 
 ///Only suport read for now.
 ///Not suport carotid3d for now.
-class StreamedVidUsImageData {
-  late VidUsDataStreamReader _reader;
+class HttpVidUsImageData {
+  late VidUsDataHttpReader _reader;
   late List<int> _imagePositionList;
   late int _version;
   late int _imageCount;
@@ -88,8 +88,7 @@ class StreamedVidUsImageData {
   late Uint8List _extendedData;
   late int _readTimeout;
 
-  final Duration DelayDuration = const Duration(milliseconds: 10);
-
+  final Duration delayDuration = const Duration(milliseconds: 10);
   final String header = "VINNO IMAGE DATA";
 
   /// Gets the version of this image data.
@@ -108,9 +107,9 @@ class StreamedVidUsImageData {
   Uint8List get extendedData => _extendedData;
 
   /// Create a VINNO Image Data.
-  StreamedVidUsImageData(Stream<Uint8List> stream, [int readTimeout = 1000]) {
+  HttpVidUsImageData(String url, {DownloadCallback? downloadCallback, int readTimeout = 1000}) {
     _readTimeout = readTimeout;
-    _reader = VidUsDataStreamReader(stream);
+    _reader = VidUsDataHttpReader(url, downloadCallback: downloadCallback);
   }
 
   //Initialize the StreamedVidUsImageData
@@ -133,7 +132,7 @@ class StreamedVidUsImageData {
     }
   }
 
-  //Read header from stream.
+  //Read header from http data.
   Future<String> _readHeader() async {
     var timeout = 0;
     while (true) {
@@ -145,8 +144,8 @@ class StreamedVidUsImageData {
           if (timeout >= _readTimeout) {
             throw ReadTimeoutException();
           }
-          await Future.delayed(DelayDuration);
-          timeout += DelayDuration.inMilliseconds;
+          await Future.delayed(delayDuration);
+          timeout += delayDuration.inMilliseconds;
         } else {
           rethrow;
         }
@@ -154,7 +153,7 @@ class StreamedVidUsImageData {
     }
   }
 
-  //Read the version from the stream.
+  //Read the version from http data.
   Future<int> _readVersion() async {
     var timeout = 0;
     while (true) {
@@ -166,8 +165,8 @@ class StreamedVidUsImageData {
           if (timeout >= _readTimeout) {
             throw ReadTimeoutException();
           }
-          await Future.delayed(DelayDuration);
-          timeout += DelayDuration.inMilliseconds;
+          await Future.delayed(delayDuration);
+          timeout += delayDuration.inMilliseconds;
         } else {
           rethrow;
         }
@@ -175,7 +174,7 @@ class StreamedVidUsImageData {
     }
   }
 
-  //Read the probe info from the stream.
+  //Read the probe info from http data.
   Future<VidUsProbe> _readProbe() async {
     var timeout = 0;
     while (true) {
@@ -187,8 +186,8 @@ class StreamedVidUsImageData {
           if (timeout >= _readTimeout) {
             throw ReadTimeoutException();
           }
-          await Future.delayed(DelayDuration);
-          timeout += DelayDuration.inMilliseconds;
+          await Future.delayed(delayDuration);
+          timeout += delayDuration.inMilliseconds;
         } else {
           rethrow;
         }
@@ -196,7 +195,7 @@ class StreamedVidUsImageData {
     }
   }
 
-  //Read the image format from the stream.
+  //Read the image format from http data.
   Future<VidUsImageFormat> _readImageFormat() async {
     var timeout = 0;
     while (true) {
@@ -207,8 +206,8 @@ class StreamedVidUsImageData {
           if (timeout >= _readTimeout) {
             throw ReadTimeoutException();
           }
-          await Future.delayed(DelayDuration);
-          timeout += DelayDuration.inMilliseconds;
+          await Future.delayed(delayDuration);
+          timeout += delayDuration.inMilliseconds;
         } else {
           rethrow;
         }
@@ -216,7 +215,7 @@ class StreamedVidUsImageData {
     }
   }
 
-  //Read extended data from the stream.
+  //Read extended data from http data.
   Future<Uint8List> _readExtendedData() async {
     var timeout = 0;
     while (true) {
@@ -227,8 +226,8 @@ class StreamedVidUsImageData {
           if (timeout >= _readTimeout) {
             throw ReadTimeoutException();
           }
-          await Future.delayed(DelayDuration);
-          timeout += DelayDuration.inMilliseconds;
+          await Future.delayed(delayDuration);
+          timeout += delayDuration.inMilliseconds;
         } else {
           rethrow;
         }
@@ -236,7 +235,7 @@ class StreamedVidUsImageData {
     }
   }
 
-  //Read the image positions data from the stream. 
+  //Read the image positions data from http data. 
   Future<Uint8List> _readImagePositionListData() async {
     var timeout = 0;
     while (true) {
@@ -247,8 +246,8 @@ class StreamedVidUsImageData {
           if (timeout >= _readTimeout) {
             throw ReadTimeoutException();
           }
-          await Future.delayed(DelayDuration);
-          timeout += DelayDuration.inMilliseconds;
+          await Future.delayed(delayDuration);
+          timeout += delayDuration.inMilliseconds;
         } else {
           rethrow;
         }
@@ -272,8 +271,8 @@ class StreamedVidUsImageData {
           if (timeout >= _readTimeout) {
             throw ReadTimeoutException();
           }
-          await Future.delayed(DelayDuration);
-          timeout += DelayDuration.inMilliseconds;
+          await Future.delayed(delayDuration);
+          timeout += delayDuration.inMilliseconds;
         } else {
           rethrow;
         }

+ 2 - 39
pubspec.yaml

@@ -1,53 +1,16 @@
 name: vid
 description: vid data reader/writer for flutter.
-version: 0.0.1
+version: 1.0.1
 homepage:
 
 environment:
   sdk: ">=2.12.0 <3.0.0"
-  flutter: ">=1.17.0"
 
 dependencies:
   flutter:
     sdk: flutter
+  dio: ^4.0.6
 
 dev_dependencies:
   flutter_test:
     sdk: flutter
-
-# For information on the generic Dart part of this file, see the
-# following page: https://dart.dev/tools/pub/pubspec
-
-# The following section is specific to Flutter.
-flutter:
-
-  # To add assets to your package, add an assets section, like this:
-  # assets:
-  #   - images/a_dot_burr.jpeg
-  #   - images/a_dot_ham.jpeg
-  #
-  # For details regarding assets in packages, see
-  # https://flutter.dev/assets-and-images/#from-packages
-  #
-  # An image asset can refer to one or more resolution-specific "variants", see
-  # https://flutter.dev/assets-and-images/#resolution-aware.
-
-  # To add custom fonts to your package, add a fonts section here,
-  # in this "flutter" section. Each entry in this list should have a
-  # "family" key with the font family name, and a "fonts" key with a
-  # list giving the asset and other descriptors for the font. For
-  # example:
-  # fonts:
-  #   - family: Schyler
-  #     fonts:
-  #       - asset: fonts/Schyler-Regular.ttf
-  #       - asset: fonts/Schyler-Italic.ttf
-  #         style: italic
-  #   - family: Trajan Pro
-  #     fonts:
-  #       - asset: fonts/TrajanPro.ttf
-  #       - asset: fonts/TrajanPro_Bold.ttf
-  #         weight: 700
-  #
-  # For details regarding fonts in packages, see
-  # https://flutter.dev/custom-fonts/#from-packages