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

fix(storage, web): fix putData when using UInt8List #13466

Merged
merged 1 commit into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
32 changes: 32 additions & 0 deletions packages/firebase_storage/firebase_storage/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';
import 'dart:convert';
import 'dart:io' as io;

import 'package:firebase_core/firebase_core.dart';
Expand Down Expand Up @@ -42,6 +43,9 @@ enum UploadType {
/// Uploads a file from the device.
file,

/// Uploads a Uint8List to Storage.
uint8List,

/// Clears any tasks from the list.
clear,
}
Expand Down Expand Up @@ -135,6 +139,23 @@ class _TaskManager extends State<TaskManager> {
);
}

Future<UploadTask> uploadUint8List() async {
UploadTask uploadTask;

// Create a Reference to the file
Reference ref = FirebaseStorage.instance
.ref()
.child('flutter-tests')
.child('/some-json.json');

const response = '{"key": "value", "number": 42}';
final data = jsonDecode(response);

uploadTask = ref.putData(Uint8List.fromList(utf8.encode(jsonEncode(data))));

return Future.value(uploadTask);
}

/// Handles the user pressing the PopupMenuItem item.
Future<void> handleUploadType(UploadType type) async {
switch (type) {
Expand All @@ -153,6 +174,12 @@ class _TaskManager extends State<TaskManager> {
});
}
break;
case UploadType.uint8List:
final task = await uploadUint8List();
setState(() {
_uploadTasks = [..._uploadTasks, task];
});
break;
case UploadType.clear:
setState(() {
_uploadTasks = [];
Expand Down Expand Up @@ -241,6 +268,11 @@ class _TaskManager extends State<TaskManager> {
child: Text('Upload local file'),
value: UploadType.file,
),
const PopupMenuItem(
// ignore: sort_child_properties_last
child: Text('Upload Uint8List'),
value: UploadType.uint8List,
),
if (_uploadTasks.isNotEmpty)
const PopupMenuItem(
// ignore: sort_child_properties_last
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ dependencies:
firebase_storage: ^12.3.2
flutter:
sdk: flutter
image_picker: ^1.0.2
image_picker_for_web: ^2.1.12
image_picker: ^1.1.2
image_picker_for_web: ^3.0.5
web: ^1.0.0

flutter:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,9 @@ class StorageReference
/// Uploads data [blob] to the actual location with optional [metadata].
/// Returns the [UploadTask] which can be used to monitor and manage
/// the upload.
UploadTask put(dynamic blob, [UploadMetadata? metadata]) {
///
/// `blob` can be a [Uint8List] or [Blob].
UploadTask put(JSAny blob, [UploadMetadata? metadata]) {
storage_interop.UploadTaskJsImpl taskImpl;
if (metadata != null) {
taskImpl = storage_interop.uploadBytesResumable(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// BSD-style license that can be found in the LICENSE file.

import 'dart:convert';
import 'dart:js_interop';
import 'dart:typed_data';

import 'package:firebase_storage_platform_interface/firebase_storage_platform_interface.dart';
Expand Down Expand Up @@ -143,7 +144,7 @@ class ReferenceWeb extends ReferencePlatform {
return TaskWeb(
this,
_ref.put(
data,
data.toJS,
settableMetadataToFbUploadMetadata(
_cache.store(metadata),
),
Expand Down Expand Up @@ -186,18 +187,24 @@ class ReferenceWeb extends ReferencePlatform {
PutStringFormat format, [
SettableMetadata? metadata,
]) {
dynamic _data = data;
late Uint8List _data;

// The universal package is converting raw to base64, so we need to convert
// Any base64 string values into a Uint8List.
if (format == PutStringFormat.base64) {
_data = base64Decode(data);
} else if (format == PutStringFormat.base64Url) {
_data = base64Url.decode(data);
} else {
// If the format is not base64 or base64Url, we need to encode the data
// as a base64 string.
_data = Uint8List.fromList(base64Encode(utf8.encode(data)).codeUnits);
}

return TaskWeb(
this,
_ref.put(
_data,
_data.toJS,
settableMetadataToFbUploadMetadata(
_cache.store(metadata),
// md5 is computed server-side, so we don't have to unpack a potentially huge Blob.
Expand Down
66 changes: 44 additions & 22 deletions tests/integration_test/firebase_storage/reference_e2e.dart
Original file line number Diff line number Diff line change
Expand Up @@ -240,33 +240,36 @@ void setupReferenceTests() {
group(
'putData',
() {
test('uploads a file with buffer and download to check content matches',
() async {
const text = 'put data text to compare with uploaded and downloaded';
List<int> list = utf8.encode(text);
test(
'uploads a file with buffer and download to check content matches',
() async {
const text =
'put data text to compare with uploaded and downloaded';
List<int> list = utf8.encode(text);

Uint8List data = Uint8List.fromList(list);
Uint8List data = Uint8List.fromList(list);

final Reference ref =
storage.ref('flutter-tests').child('flt-ok.txt');
final Reference ref =
storage.ref('flutter-tests').child('flt-ok.txt');

final TaskSnapshot complete = await ref.putData(
data,
SettableMetadata(
contentLanguage: 'en',
),
);
final TaskSnapshot complete = await ref.putData(
data,
SettableMetadata(
contentLanguage: 'en',
),
);

expect(complete.metadata?.size, text.length);
expect(complete.metadata?.contentLanguage, 'en');
expect(complete.metadata?.size, text.length);
expect(complete.metadata?.contentLanguage, 'en');

// Download the file from Firebase Storage
final downloadedData = await ref.getData();
final downloadedContent = String.fromCharCodes(downloadedData!);
// Download the file from Firebase Storage
final downloadedData = await ref.getData();
final downloadedContent = String.fromCharCodes(downloadedData!);

// Verify that the downloaded content matches the original content
expect(downloadedContent, equals(text));
});
// Verify that the downloaded content matches the original content
expect(downloadedContent, equals(text));
},
);

//TODO(pr-mais): causes the emulator to crash
// test('errors if permission denied', () async {
Expand All @@ -282,8 +285,27 @@ void setupReferenceTests() {
// .having((e) => e.message, 'message',
// 'User is not authorized to perform the desired action.')));
// });

test(
'upload a json file',
() async {
final Map<String, dynamic> data = <String, dynamic>{
'name': 'John Doe',
'age': 30,
};
final Uint8List jsonData = utf8.encode(jsonEncode(data));
final Reference ref =
storage.ref('flutter-tests').child('flt-web-ok.json');
final TaskSnapshot complete = await ref.putData(
jsonData,
SettableMetadata(
contentType: 'application/json',
),
);
expect(complete.metadata?.contentType, 'application/json');
},
);
},
skip: kIsWeb,
);

group('putBlob', () {
Expand Down
Loading