From 04f0f6cf17c8568c2506935115a46756a8b17205 Mon Sep 17 00:00:00 2001 From: Flop Date: Tue, 1 Aug 2023 20:07:46 +0800 Subject: [PATCH 1/2] Fix Dio.download not closing RandomAccessFile when file writing fails --- dio/CHANGELOG.md | 2 +- dio/lib/src/dio/dio_for_native.dart | 5 +++++ dio/test/download_test.dart | 19 +++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/dio/CHANGELOG.md b/dio/CHANGELOG.md index 5600bb350..9fe02075e 100644 --- a/dio/CHANGELOG.md +++ b/dio/CHANGELOG.md @@ -5,7 +5,7 @@ See the [Migration Guide][] for the complete breaking changes list.** ## Unreleased -*None.* +- Fix `Dio.download` not closing `RandomAccessFile` when file writing fails. ## 5.3.1 diff --git a/dio/lib/src/dio/dio_for_native.dart b/dio/lib/src/dio/dio_for_native.dart index 521a813e0..059330290 100644 --- a/dio/lib/src/dio/dio_for_native.dart +++ b/dio/lib/src/dio/dio_for_native.dart @@ -140,6 +140,11 @@ class DioForNative with DioMixin implements Dio { }).catchError((Object e) async { try { await subscription.cancel(); + closed = true; + await raf.close(); + if (deleteOnError && file.existsSync()) { + await file.delete(); + } } finally { completer.completeError( DioMixin.assureDioException(e, response.requestOptions), diff --git a/dio/test/download_test.dart b/dio/test/download_test.dart index cdc0e4d50..3ebeafadb 100644 --- a/dio/test/download_test.dart +++ b/dio/test/download_test.dart @@ -94,6 +94,25 @@ void main() { ); }); + test('download write failed', () async { + const savePath = 'test/_download_test.md'; + final f = File(savePath)..createSync(recursive: true); + final raf = f.openSync(mode: FileMode.write)..lockSync(FileLock.exclusive); + expect(f.existsSync(), isTrue); + + final dio = Dio()..options.baseUrl = serverUrl.toString(); + await expectLater( + dio.download('/download', savePath, deleteOnError: false).catchError((e) { + throw (e as DioException).error!; + }), + throwsA(isA()), + ); + + await expectLater(raf.unlock(), completes); + await expectLater(raf.close(), completes); + await expectLater(f.delete(), completes); + }); + test('`savePath` types', () async { final testPath = p.join(Directory.systemTemp.path, 'dio', 'testPath'); From f8859e70115ec753e15bbed322a01d7d44f54c36 Mon Sep 17 00:00:00 2001 From: Flop Date: Wed, 2 Aug 2023 12:47:11 +0800 Subject: [PATCH 2/2] Fix for Dio.download not cleaning the file on data handling error --- dio/CHANGELOG.md | 2 +- dio/lib/src/dio/dio_for_native.dart | 10 +++---- dio/test/download_test.dart | 42 ++++++++++++++++++++++------- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/dio/CHANGELOG.md b/dio/CHANGELOG.md index 9fe02075e..02d42e711 100644 --- a/dio/CHANGELOG.md +++ b/dio/CHANGELOG.md @@ -5,7 +5,7 @@ See the [Migration Guide][] for the complete breaking changes list.** ## Unreleased -- Fix `Dio.download` not closing `RandomAccessFile` when file writing fails. +- Fix for `Dio.download` not cleaning the file on data handling error. ## 5.3.1 diff --git a/dio/lib/src/dio/dio_for_native.dart b/dio/lib/src/dio/dio_for_native.dart index 059330290..d16fa4124 100644 --- a/dio/lib/src/dio/dio_for_native.dart +++ b/dio/lib/src/dio/dio_for_native.dart @@ -117,9 +117,9 @@ class DioForNative with DioMixin implements Dio { if (!closed) { closed = true; await asyncWrite; - await raf.close(); + await raf.close().catchError((_) => raf); if (deleteOnError && file.existsSync()) { - await file.delete(); + await file.delete().catchError((_) => file); } } } @@ -141,9 +141,9 @@ class DioForNative with DioMixin implements Dio { try { await subscription.cancel(); closed = true; - await raf.close(); + await raf.close().catchError((_) => raf); if (deleteOnError && file.existsSync()) { - await file.delete(); + await file.delete().catchError((_) => file); } } finally { completer.completeError( @@ -156,7 +156,7 @@ class DioForNative with DioMixin implements Dio { try { await asyncWrite; closed = true; - await raf.close(); + await raf.close().catchError((_) => raf); completer.complete(response); } catch (e) { completer.completeError( diff --git a/dio/test/download_test.dart b/dio/test/download_test.dart index 3ebeafadb..334af34d2 100644 --- a/dio/test/download_test.dart +++ b/dio/test/download_test.dart @@ -94,23 +94,47 @@ void main() { ); }); - test('download write failed', () async { + test('delete on error', () async { const savePath = 'test/_download_test.md'; final f = File(savePath)..createSync(recursive: true); - final raf = f.openSync(mode: FileMode.write)..lockSync(FileLock.exclusive); expect(f.existsSync(), isTrue); final dio = Dio()..options.baseUrl = serverUrl.toString(); await expectLater( - dio.download('/download', savePath, deleteOnError: false).catchError((e) { - throw (e as DioException).error!; - }), - throwsA(isA()), + dio + .download( + '/download', + savePath, + deleteOnError: true, + onReceiveProgress: (count, total) => throw AssertionError(), + ) + .catchError((e) => throw (e as DioException).error!), + throwsA(isA()), ); + expect(f.existsSync(), isFalse); + }); - await expectLater(raf.unlock(), completes); - await expectLater(raf.close(), completes); - await expectLater(f.delete(), completes); + test('delete on cancel', () async { + const savePath = 'test/_download_test.md'; + final f = File(savePath)..createSync(recursive: true); + expect(f.existsSync(), isTrue); + + final cancelToken = CancelToken(); + final dio = Dio()..options.baseUrl = serverUrl.toString(); + await expectLater( + dio + .download( + '/download', + savePath, + deleteOnError: true, + cancelToken: cancelToken, + onReceiveProgress: (count, total) => cancelToken.cancel(), + ) + .catchError((e) => throw (e as DioException).type), + throwsA(DioExceptionType.cancel), + ); + await Future.delayed(const Duration(milliseconds: 100)); + expect(f.existsSync(), isFalse); }); test('`savePath` types', () async {