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

[firebase_storage]: Upload fails with Uint8List is not a subtype of type 'JSValue' in type cast on Wasm #13458

Open
1 task done
rayliverified opened this issue Oct 2, 2024 · 7 comments
Labels
Needs Attention This issue needs maintainer attention. platform: web Issues / PRs which are specifically for web. plugin: storage type: bug Something isn't working

Comments

@rayliverified
Copy link

rayliverified commented Oct 2, 2024

Is there an existing issue for this?

  • I have searched the existing issues.

Which plugins are affected?

Storage

Which platforms are affected?

Web

Description

Upload fails on Wasm with type error internally.

Sample Repo: https://github.com/rayliverified/flutter_storage_example

Screenshot 2024-10-02 at 12 13 06 PM

Reproducing the issue

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

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';

import 'firebase_options.dart';

void main() async {
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );

  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool _isUploading = false;

  Future<void> uploadToFirebaseStorage() async {
    setState(() {
      _isUploading = true;
    });

    try {
      // Simulating API response
      final response = '{"key": "value", "number": 42}';
      final data = jsonDecode(response);

      final ref = FirebaseStorage.instance
          .ref()
          .child('file.json');

      await ref.putData(
        Uint8List.fromList(utf8.encode(jsonEncode(data))),
      );

      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Upload successful!')),
      );
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Upload failed: $e')),
      );
    } finally {
      setState(() {
        _isUploading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: _isUploading ? null : uploadToFirebaseStorage,
              child: _isUploading
                  ? const CircularProgressIndicator()
                  : const Text('Upload to Firebase Storage'),
            ),
          ],
        ),
      ),
    );
  }
}

Firebase Core version

3.7.0

Flutter Version

3.24.3

Relevant Log Output

No response

Flutter dependencies

Expand Flutter dependencies snippet
Dart SDK 3.5.3
Flutter SDK 3.24.3
flutter_storage_example 1.0.0+1

dependencies:
- cupertino_icons 1.0.8
- firebase_core 3.6.0 [firebase_core_platform_interface firebase_core_web flutter meta]
- firebase_storage 12.3.2 [firebase_core firebase_core_platform_interface firebase_storage_platform_interface firebase_storage_web flutter]
- flutter 0.0.0 [characters collection material_color_utilities meta vector_math sky_engine]

dev dependencies:
- flutter_lints 4.0.0 [lints]
- flutter_test 0.0.0 [flutter test_api matcher path fake_async clock stack_trace vector_math leak_tracker_flutter_testing async boolean_selector characters collection leak_tracker leak_tracker_testing material_color_utilities meta source_span stream_channel string_scanner term_glyph vm_service]

transitive dependencies:
- _flutterfire_internals 1.3.44 [collection firebase_core firebase_core_platform_interface flutter meta]
- async 2.11.0 [collection meta]
- boolean_selector 2.1.1 [source_span string_scanner]
- characters 1.3.0
- clock 1.1.1
- collection 1.18.0
- fake_async 1.3.1 [clock collection]
- firebase_core_platform_interface 5.3.0 [collection flutter flutter_test meta plugin_platform_interface]
- firebase_core_web 2.18.1 [firebase_core_platform_interface flutter flutter_web_plugins meta web]
- firebase_storage_platform_interface 5.1.31 [_flutterfire_internals collection firebase_core flutter meta plugin_platform_interface]
- firebase_storage_web 3.10.2 [_flutterfire_internals async firebase_core firebase_core_web firebase_storage_platform_interface flutter flutter_web_plugins http meta web]
- flutter_web_plugins 0.0.0 [flutter characters collection material_color_utilities meta vector_math]
- http 1.2.2 [async http_parser meta web]
- http_parser 4.0.2 [collection source_span string_scanner typed_data]
- leak_tracker 10.0.5 [clock collection meta path vm_service]
- leak_tracker_flutter_testing 3.0.5 [flutter leak_tracker leak_tracker_testing matcher meta]
- leak_tracker_testing 3.0.1 [leak_tracker matcher meta]
- lints 4.0.0
- matcher 0.12.16+1 [async meta stack_trace term_glyph test_api]
- material_color_utilities 0.11.1 [collection]
- meta 1.15.0
- path 1.9.0
- plugin_platform_interface 2.1.8 [meta]
- sky_engine 0.0.99
- source_span 1.10.0 [collection path term_glyph]
- stack_trace 1.11.1 [path]
- stream_channel 2.1.2 [async]
- string_scanner 1.2.0 [source_span]
- term_glyph 1.2.1
- test_api 0.7.2 [async boolean_selector collection meta source_span stack_trace stream_channel string_scanner term_glyph]
- typed_data 1.3.2 [collection]
- vector_math 2.1.4
- vm_service 14.2.5
- web 1.1.0

Additional context and comments

No response

@rayliverified rayliverified added Needs Attention This issue needs maintainer attention. type: bug Something isn't working labels Oct 2, 2024
@rayliverified
Copy link
Author

@kevmoo CC

@kevmoo
Copy link
Collaborator

kevmoo commented Oct 2, 2024

Would you include a stack trace? Do you see anything in the chrome console?

@rayliverified
Copy link
Author

Type 'Uint8List' is not a subtype of type 'JSValue' in type cast
main.dart.mjs:19     at _TypeError._throwAsCheckError (http://localhost:56303/main.dart.wasm:wasm-function[898]:0xec482)
    at _asSubtype (http://localhost:56303/main.dart.wasm:wasm-function[934]:0xecee2)
    at StorageReference.put (http://localhost:56303/main.dart.wasm:wasm-function[13654]:0x1e0757)
    at ReferenceWeb.putData (http://localhost:56303/main.dart.wasm:wasm-function[13624]:0x1e01f6)
    at Reference.putData (http://localhost:56303/main.dart.wasm:wasm-function[12098]:0x1c0b30)
    at _MyHomePageState.uploadToFirebaseStorage inner (http://localhost:56303/main.dart.wasm:wasm-function[12089]:0x1c098a)
    at _MyHomePageState.uploadToFirebaseStorage (http://localhost:56303/main.dart.wasm:wasm-function[12084]:0x1c0833)
    at _MyHomePageState.uploadToFirebaseStorage tear-off trampoline (http://localhost:56303/main.dart.wasm:wasm-function[12086]:0x1c085f)
main.dart.wasm:0xea9c0 Uncaught Exception {}

@SelaseKay SelaseKay added plugin: storage platform: web Issues / PRs which are specifically for web. labels Oct 3, 2024
@rayliverified
Copy link
Author

Uh oh, the issue affects image and file uploads as well.

This isn't limited to data types. Image uploads don't work either.

@SelaseKay
Copy link
Contributor

Hi @rayliverified , thanks for submitting this issue. I'm looking into it.

@SelaseKay
Copy link
Contributor

I'm able to reproduce this issue.

Steps to reproduce:
Run the code below with flutter run --wasm on web.

Reproducible code:

import 'dart:async';
import 'dart:convert';

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import 'firebase_options.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );

  runApp(MyApp());
}

/// Enum representing the upload task types the example app supports.
enum UploadType {
  /// Uploads a randomly generated string (as a file) to Storage.
  string,

  /// Uploads a file from the device.
  file,

  /// Clears any tasks from the list.
  clear,
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool _isUploading = false;

  Future<void> uploadToFirebaseStorage() async {
    setState(() {
      _isUploading = true;
    });

    try {
      // Simulating API response
      final response = '{"key": "value", "number": 42}';
      final data = jsonDecode(response);

      final ref = FirebaseStorage.instance
          .ref()
          .child('flutter-tests')
          .child('/file.json');

      await ref.putData(
        Uint8List.fromList(utf8.encode(jsonEncode(data))),
      );

      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Upload successful!')),
      );
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Upload failed: $e')),
      );
    } finally {
      setState(() {
        _isUploading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: _isUploading ? null : uploadToFirebaseStorage,
              child: _isUploading
                  ? const CircularProgressIndicator()
                  : const Text('Upload to Firebase Storage'),
            ),
          ],
        ),
      ),
    );
  }
}

@kevmoo
Copy link
Collaborator

kevmoo commented Oct 4, 2024

We need more testing here! There is a dynamic flowing through that needs to be turned into a JS type.

For the ambitious, here's a patch to try to apply and validate.

diff --git a/packages/firebase_storage/firebase_storage_web/lib/src/interop/storage.dart b/packages/firebase_storage/firebase_storage_web/lib/src/interop/storage.dart
index d0c0ba5c2..e5e9c3416 100644
--- a/packages/firebase_storage/firebase_storage_web/lib/src/interop/storage.dart
+++ b/packages/firebase_storage/firebase_storage_web/lib/src/interop/storage.dart
@@ -179,7 +179,7 @@ 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]) {
+  UploadTask put(JSObject blob, [UploadMetadata? metadata]) {
     storage_interop.UploadTaskJsImpl taskImpl;
     if (metadata != null) {
       taskImpl = storage_interop.uploadBytesResumable(
diff --git a/packages/firebase_storage/firebase_storage_web/lib/src/interop/storage_interop.dart b/packages/firebase_storage/firebase_storage_web/lib/src/interop/storage_interop.dart
index 064610e90..d8c9a9d52 100644
--- a/packages/firebase_storage/firebase_storage_web/lib/src/interop/storage_interop.dart
+++ b/packages/firebase_storage/firebase_storage_web/lib/src/interop/storage_interop.dart
@@ -67,7 +67,7 @@ external JSPromise /* FullMetadataJsImpl */ updateMetadata(
 @JS()
 @staticInterop
 external UploadTaskJsImpl uploadBytesResumable(
-    ReferenceJsImpl ref, JSAny /* Blob | Uint8Array | ArrayBuffer */ data,
+    ReferenceJsImpl ref, JSObject /* Blob | Uint8Array | ArrayBuffer */ data,
     [UploadMetadataJsImpl? metadata]);
 
 @JS()
diff --git a/packages/firebase_storage/firebase_storage_web/lib/src/reference_web.dart b/packages/firebase_storage/firebase_storage_web/lib/src/reference_web.dart
index 2ba54062b..7069ce5c1 100644
--- a/packages/firebase_storage/firebase_storage_web/lib/src/reference_web.dart
+++ b/packages/firebase_storage/firebase_storage_web/lib/src/reference_web.dart
@@ -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';
@@ -143,7 +144,7 @@ class ReferenceWeb extends ReferencePlatform {
     return TaskWeb(
       this,
       _ref.put(
-        data,
+        data.toJS,
         settableMetadataToFbUploadMetadata(
           _cache.store(metadata),
         ),
@@ -155,9 +156,7 @@ class ReferenceWeb extends ReferencePlatform {
   ///
   /// Optionally, you can also set metadata onto the uploaded object.
   @override
-  TaskPlatform putBlob(dynamic data, [SettableMetadata? metadata]) {
-    assert(data is web.Blob, 'data must be a package:web Blob object.');
-
+  TaskPlatform putBlob(covariant web.Blob data, [SettableMetadata? metadata]) {
     return TaskWeb(
       this,
       _ref.put(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs Attention This issue needs maintainer attention. platform: web Issues / PRs which are specifically for web. plugin: storage type: bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants