vid_us_data_http_reader.dart 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. import 'dart:typed_data';
  2. import 'package:dio/dio.dart';
  3. import 'package:flutter/foundation.dart';
  4. //Raised when data is not ready.
  5. class NotReadyException implements Exception {}
  6. //Raised when errro happened during the downloading.
  7. class DownloadErrorException implements Exception {
  8. String _msg;
  9. DownloadErrorException(this._msg);
  10. @override
  11. String toString() {
  12. return _msg;
  13. }
  14. }
  15. typedef DownloadCallback = void Function(double progress);
  16. class VidUsDataHttpReader {
  17. late final List<int> _data = [];
  18. late int _index;
  19. late ByteBuffer _buffer;
  20. late bool _error;
  21. late String _errorMsg;
  22. late String _url;
  23. late int _chunkSize;
  24. late Dio _dio;
  25. DownloadCallback? downloadCallback;
  26. VidUsDataHttpReader(String url, {this.downloadCallback, int chunkSize = 32768}) {
  27. _chunkSize = chunkSize;
  28. _url = url;
  29. _error = false;
  30. _errorMsg = '';
  31. _buffer = Uint8List.fromList(_data).buffer;
  32. _index = 0;
  33. _dio = Dio();
  34. //TODO let the user set the timeout.
  35. _dio.options.connectTimeout = 5000; //5S
  36. _dio.options.receiveTimeout = 3000;
  37. _startDownload();
  38. }
  39. void _startDownload() async {
  40. //get file info
  41. var fileSize = await _getFileSize();
  42. if (!_error) {
  43. var last = fileSize % _chunkSize;
  44. var chunkCount = fileSize ~/ _chunkSize;
  45. for (var i = 0; i < chunkCount; i++) {
  46. var start = i * _chunkSize;
  47. var end = start + _chunkSize;
  48. await _downloadChunk(start, end);
  49. if (_error) {
  50. break;
  51. }
  52. if (downloadCallback != null) {
  53. var progress = _data.length / fileSize;
  54. downloadCallback!(progress);
  55. }
  56. }
  57. if (!_error && last > 0) {
  58. var start = chunkCount * _chunkSize;
  59. var end = start + last;
  60. await _downloadChunk(start, end);
  61. if (!_error && downloadCallback != null) {
  62. var progress = _data.length / fileSize;
  63. downloadCallback!(progress);
  64. }
  65. }
  66. }
  67. }
  68. Future<int> _getFileSize() async {
  69. var response = await _dio.head(_url);
  70. if (response.statusCode != 200) {
  71. _error = true;
  72. _errorMsg = 'Get file info error.';
  73. return 0;
  74. } else {
  75. var contentLength = response.headers.value(Headers.contentLengthHeader);
  76. if (contentLength == null) {
  77. _error = true;
  78. _errorMsg = 'No Content-Length from headers.';
  79. return 0;
  80. } else {
  81. var fileSize = int.parse(contentLength);
  82. return fileSize;
  83. }
  84. }
  85. }
  86. Future _downloadChunk(int chunkStart, int chunkEnd) async {
  87. var response = await _dio.get<List<int>>(_url, options: Options(responseType: ResponseType.bytes, headers: {"range": "bytes=$chunkStart-$chunkEnd"}));
  88. if (response.statusCode == 206) {
  89. var downloadedData = response.data;
  90. if (downloadedData == null) {
  91. _error = true;
  92. _errorMsg = 'Download chunk error.';
  93. } else {
  94. //Update the buffer.
  95. _data.addAll(downloadedData);
  96. _buffer = Uint8List.fromList(_data).buffer;
  97. }
  98. } else {
  99. _error = true;
  100. _errorMsg = 'Target url does not support download with chunks.';
  101. }
  102. }
  103. //Close the reader and its http connection.
  104. void close(){
  105. _dio.close(force: true);
  106. }
  107. ///Read int value from binary data(little endian),
  108. int readInt([int index = -1]) {
  109. if (_error) {
  110. throw DownloadErrorException(_errorMsg);
  111. }
  112. if (index == -1) {
  113. if ((_buffer.lengthInBytes - _index) < 4) {
  114. throw NotReadyException();
  115. }
  116. var intData = _buffer.asByteData(_index, 4);
  117. _index += 4;
  118. return intData.getInt32(0, Endian.little);
  119. } else {
  120. if ((_buffer.lengthInBytes - index) < 4) {
  121. throw NotReadyException();
  122. }
  123. var intData = _buffer.asByteData(index, 4);
  124. _index = index + 4;
  125. return intData.getInt32(0, Endian.little);
  126. }
  127. }
  128. ///Read string from the binary data, the string format is utf-16, which is unicode in C#
  129. String readString([int index = -1]) {
  130. if (_error) {
  131. throw DownloadErrorException(_errorMsg);
  132. }
  133. int dataLength;
  134. if (index == -1) {
  135. dataLength = readInt();
  136. if ((_buffer.lengthInBytes - _index) < dataLength) {
  137. throw NotReadyException();
  138. }
  139. } else {
  140. dataLength = readInt(index);
  141. if ((_buffer.lengthInBytes - index) < dataLength) {
  142. throw NotReadyException();
  143. }
  144. }
  145. var stringData = _buffer.asUint8List(_index, dataLength);
  146. _index += dataLength;
  147. var unicodeStringData = Uint8List.fromList(stringData.toList()).buffer.asUint16List();
  148. return String.fromCharCodes(unicodeStringData);
  149. }
  150. ///Read int16 value from binary data(little endian)
  151. int readInt16([int index = -1]) {
  152. if (_error) {
  153. throw DownloadErrorException(_errorMsg);
  154. }
  155. if (index == -1) {
  156. if ((_buffer.lengthInBytes - _index) < 2) {
  157. throw NotReadyException();
  158. }
  159. var intData = _buffer.asByteData(_index, 2);
  160. _index += 2;
  161. return intData.getInt16(0, Endian.little);
  162. } else {
  163. if ((_buffer.lengthInBytes - index) < 2) {
  164. throw NotReadyException();
  165. }
  166. var intData = _buffer.asByteData(index, 2);
  167. _index = index + 2;
  168. return intData.getInt16(0, Endian.little);
  169. }
  170. }
  171. ///Read int64 value from binary data(little endian)
  172. ///this method is not support in web platform use readInt64V2 instead.
  173. int readInt64([int index = -1]) {
  174. if (_error) {
  175. throw DownloadErrorException(_errorMsg);
  176. }
  177. if (index == -1) {
  178. if ((_buffer.lengthInBytes - _index) < 8) {
  179. throw NotReadyException();
  180. }
  181. var intData = _buffer.asByteData(_index, 8);
  182. _index += 8;
  183. return intData.getInt64(0, Endian.little);
  184. } else {
  185. if ((_buffer.lengthInBytes - index) < 8) {
  186. throw NotReadyException();
  187. }
  188. var intData = _buffer.asByteData(index, 8);
  189. _index = index + 8;
  190. return intData.getInt64(0, Endian.little);
  191. }
  192. }
  193. ///Read int64 value from binary data(little endian)
  194. ///this method use two int32 to support read int64 on web platform
  195. int readInt64V2([int index = -1]) {
  196. if (_error) {
  197. throw DownloadErrorException(_errorMsg);
  198. }
  199. if (index == -1) {
  200. if ((_buffer.lengthInBytes - _index) < 8) {
  201. throw NotReadyException();
  202. }
  203. int low = readInt();
  204. int high = readInt();
  205. int value = high >> 32;
  206. value |= low;
  207. return value;
  208. } else {
  209. if ((_buffer.lengthInBytes - index) < 8) {
  210. throw NotReadyException();
  211. }
  212. int low = readInt(index);
  213. //Read the next int.
  214. int high = readInt();
  215. int value = high >> 32;
  216. value |= low;
  217. return value;
  218. }
  219. }
  220. ///Read float value from binary data(little endian)
  221. double readFloat([int index = -1]) {
  222. if (_error) {
  223. throw DownloadErrorException(_errorMsg);
  224. }
  225. if (index == -1) {
  226. if ((_buffer.lengthInBytes - _index) < 4) {
  227. throw NotReadyException();
  228. }
  229. var floatData = _buffer.asByteData(_index, 4);
  230. _index += 4;
  231. return floatData.getFloat32(0, Endian.little);
  232. } else {
  233. if ((_buffer.lengthInBytes - index) < 4) {
  234. throw NotReadyException();
  235. }
  236. var floatData = _buffer.asByteData(index, 4);
  237. _index = index + 4;
  238. return floatData.getFloat32(0, Endian.little);
  239. }
  240. }
  241. ///Read double value from binary data(little endian)
  242. double readDouble([int index = -1]) {
  243. if (_error) {
  244. throw DownloadErrorException(_errorMsg);
  245. }
  246. if (index == -1) {
  247. if ((_buffer.lengthInBytes - _index) < 8) {
  248. throw NotReadyException();
  249. }
  250. var floatData = _buffer.asByteData(_index, 8);
  251. _index += 8;
  252. return floatData.getFloat64(0, Endian.little);
  253. } else {
  254. if ((_buffer.lengthInBytes - index) < 8) {
  255. throw NotReadyException();
  256. }
  257. var floatData = _buffer.asByteData(index, 8);
  258. _index = index + 8;
  259. return floatData.getFloat64(0, Endian.little);
  260. }
  261. }
  262. ///Read bool value from binary data
  263. bool readBool([int index = -1]) {
  264. if (_error) {
  265. throw DownloadErrorException(_errorMsg);
  266. }
  267. if (index == -1) {
  268. if ((_buffer.lengthInBytes - _index) < 1) {
  269. throw NotReadyException();
  270. }
  271. var boolData = _buffer.asByteData(_index, 1);
  272. _index++;
  273. return boolData.getInt8(0) == 1;
  274. } else {
  275. if ((_buffer.lengthInBytes - index) < 1) {
  276. throw NotReadyException();
  277. }
  278. var boolData = _buffer.asByteData(index, 1);
  279. _index = index + 1;
  280. return boolData.getInt8(0) == 1;
  281. }
  282. }
  283. ///Read byte value from binary data
  284. int readByte([int index = -1]) {
  285. if (_error) {
  286. throw DownloadErrorException(_errorMsg);
  287. }
  288. if (index == -1) {
  289. if ((_buffer.lengthInBytes - _index) < 1) {
  290. throw NotReadyException();
  291. }
  292. var byteData = _buffer.asByteData(_index, 1);
  293. _index++;
  294. return byteData.getInt8(0);
  295. } else {
  296. if ((_buffer.lengthInBytes - index) < 1) {
  297. throw NotReadyException();
  298. }
  299. var byteData = _buffer.asByteData(index, 1);
  300. _index = index + 1;
  301. return byteData.getInt8(0);
  302. }
  303. }
  304. ///Read bytes from the binary data.
  305. Uint8List readBytes([int index = -1]) {
  306. if (_error) {
  307. throw DownloadErrorException(_errorMsg);
  308. }
  309. var dataLength = readInt(index);
  310. if ((_buffer.lengthInBytes - _index) < dataLength) {
  311. throw NotReadyException();
  312. }
  313. var bytes = _buffer.asUint8List(_index, dataLength);
  314. _index += dataLength;
  315. return bytes;
  316. }
  317. }