Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for download function in web #2230

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions dio/lib/src/options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ enum ResponseType {

/// Get the original bytes, the [Response.data] will be [List<int>].
bytes,

/// Get blob url.
/// This value is needed when you need content in the form of a blob url.
/// Only work in web.
///
/// the [Response.data] will be [String].
blobUrl,
}

/// {@template dio.options.ListFormat}
Expand Down
41 changes: 28 additions & 13 deletions plugins/web_adapter/lib/src/adapter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ class BrowserHttpClientAdapter implements HttpClientAdapter {
xhrs.add(xhr);
xhr
..open(options.method, '${options.uri}')
..responseType = 'arraybuffer';
..responseType =
options.responseType == ResponseType.blobUrl ? 'blob' : 'arraybuffer';

final withCredentialsOption = options.extra['withCredentials'];
if (withCredentialsOption != null) {
Expand Down Expand Up @@ -66,18 +67,32 @@ class BrowserHttpClientAdapter implements HttpClientAdapter {
final completer = Completer<ResponseBody>();

xhr.onLoad.first.then((_) {
final ByteBuffer body = (xhr.response as JSArrayBuffer).toDart;
completer.complete(
ResponseBody.fromBytes(
body.asUint8List(),
xhr.status,
headers: xhr.getResponseHeaders(),
statusMessage: xhr.statusText,
isRedirect: xhr.status == 302 ||
xhr.status == 301 ||
options.uri.toString() != xhr.responseURL,
),
);
if (options.responseType == ResponseType.blobUrl) {
completer.complete(
ResponseBody.fromString(
web.URL.createObjectURL(xhr.response as web.Blob),
xhr.status,
headers: xhr.getResponseHeaders(),
statusMessage: xhr.statusText,
isRedirect: xhr.status == 302 ||
xhr.status == 301 ||
options.uri.toString() != xhr.responseURL,
),
);
} else {
final ByteBuffer body = (xhr.response as JSArrayBuffer).toDart;
completer.complete(
ResponseBody.fromBytes(
body.asUint8List(),
xhr.status,
headers: xhr.getResponseHeaders(),
statusMessage: xhr.statusText,
isRedirect: xhr.status == 302 ||
xhr.status == 301 ||
options.uri.toString() != xhr.responseURL,
),
);
}
});

Timer? connectTimeoutTimer;
Expand Down
23 changes: 20 additions & 3 deletions plugins/web_adapter/lib/src/dio_impl.dart
Copy link
Member

@AlexV525 AlexV525 Dec 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What will the result be after downloaded? Could you demonstrate how it works?

EDIT: Please also add an example somewhere in our examples so everyone can run and see how it works.

Copy link
Author

@mbfakourii mbfakourii Jan 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added an example in example_flutter_app.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To test on the local web, make sure CORS is disabled.

and use local dio and dio_web_adapter in example_flutter_app pubspec

dependency_overrides:
  dio_web_adapter:
    path: ...\dio\plugins\web_adapter
  dio:
    path: ...\dio\dio

Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:async';

import 'package:dio/dio.dart';

import 'adapter.dart';
Expand Down Expand Up @@ -25,9 +27,24 @@ class DioForBrowser with DioMixin implements Dio {
String lengthHeader = Headers.contentLengthHeader,
Object? data,
Options? options,
}) {
throw UnsupportedError(
'The download method is not available in the Web environment.',
}) async {
final Response response = await fetch(
RequestOptions(
baseUrl: urlPath,
data: data,
method: 'GET',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why always GET?

Copy link
Author

@mbfakourii mbfakourii Jan 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.

responseType: ResponseType.blobUrl,
queryParameters: queryParameters,
cancelToken: cancelToken,
onReceiveProgress: onReceiveProgress,
),
);

final completer = Completer<Response>();

// Set response in Completer
completer.complete(response);

return completer.future;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are unnecessary.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed.

}
}
14 changes: 14 additions & 0 deletions plugins/web_adapter/test/browser_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,18 @@ void main() {
browserAdapter.fetch(opts, testStream, cancelFuture);
expect(browserAdapter.xhrs.every((e) => e.withCredentials == true), isTrue);
});

test('ResponseType in blobUrl', () async {
final browserAdapter = BrowserHttpClientAdapter(withCredentials: true);
final opts = RequestOptions(responseType : ResponseType.blobUrl);
final testStream = Stream<Uint8List>.periodic(
const Duration(seconds: 1),
(x) => Uint8List(x),
);
final cancelFuture = opts.cancelToken?.whenCancel;

browserAdapter.fetch(opts, testStream, cancelFuture);
expect(browserAdapter.xhrs.every((e) => e.withCredentials == true), isTrue);
expect(opts.responseType, ResponseType.blobUrl);
});
}
Loading