You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter Web用universal_io传图至Supabase Storage遇UnimplementedError

Flutter Web 上传图片至Supabase Storage 报错解决

问题说明

在Flutter Web环境中尝试上传图片到Supabase Storage,使用官方上传方法:

supabase.storage.from('avatars').upload(fileName, file)

通过universal_io包构造File对象:

var file = File(_session.photoFilePath!); // import 'package:universal_io/io.dart';

打印的文件路径为blob:http://localhost:55914/eff9c2bb-061f-4577-bf00-eff523029635,文件名为tiger.jpeg。已完成登录且拥有avatars存储桶上传权限,但执行上传时抛出UnimplementedError,错误栈如下:

UnimplementedError
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 266:49      throw_
packages/universal_io/src/io/file.dart 225:7                                      new
packages/fr_panel/view/session_page.dart 351:42                                   <fn>
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 84:54                runBody
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 123:5                _async
packages/fr_panel/view/session_page.dart 348:38                                   <fn>
packages/fr_panel/widgets/next_button.dart 40:27                                  <fn>
packages/flutter/src/widgets/framework.dart 1114:30                               setState
packages/fr_panel/widgets/next_button.dart 39:9                                   <fn>
packages/flutter/src/material/ink_well.dart 1072:21                               handleTap
packages/flutter/src/gestures/recognizer.dart 253:24                              invokeCallback
packages/flutter/src/gestures/tap.dart 627:11                                     handleTapUp
packages/flutter/src/gestures/tap.dart 306:5                                      [_checkUp]
packages/flutter/src/gestures/tap.dart 239:7                                      handlePrimaryPointer
packages/flutter/src/gestures/recognizer.dart 615:9                               handleEvent
packages/flutter/src/gestures/pointer_router.dart 98:12                           [_dispatch]
packages/flutter/src/gestures/pointer_router.dart 143:9                           <fn>
dart-sdk/lib/_internal/js_dev_runtime/private/linked_hash_map.dart 21:13          forEach
packages/flutter/src/gestures/pointer_router.dart 141:17                          [_dispatchEventToRoutes]
packages/flutter/src/gestures/pointer_router.dart 127:7                           route
packages/flutter/src/gestures/binding.dart 460:19                                 handleEvent
packages/flutter/src/gestures/binding.dart 440:14                                 dispatchEvent
packages/flutter/src/rendering/binding.dart 337:11                                dispatchEvent
packages/flutter/src/gestures/binding.dart 395:7                                  [_handlePointerEventImmediately]
packages/flutter/src/gestures/binding.dart 357:5                                  handlePointerEvent
packages/flutter/src/gestures/binding.dart 314:7                                  [_flushPointerEventQueue]
packages/flutter/src/gestures/binding.dart 295:7                                  [_handlePointerDataPacket]
lib/_engine/engine/platform_dispatcher.dart 1183:13                               invoke1
lib/_engine/engine/platform_dispatcher.dart 244:5                                 invokeOnPointerDataPacket
lib/_engine/engine/pointer_binding.dart 147:39                                    [_onPointerData]
lib/_engine/engine/pointer_binding.dart 653:20                                    <fn>
lib/_engine/engine/pointer_binding.dart 594:14                                    <fn>
lib/_engine/engine/pointer_binding.dart 288:16                                    loggedHandler
lib/_engine/engine/pointer_binding.dart 179:80                                    <fn>
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 334:14  _checkAndCall
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 339:39  dcall

使用的Flutter环境:

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel master, 3.1.0-0.0.pre.1698, on macOS 13.0 22A5286j darwin-arm, locale en-TR)
[✓] Android toolchain - develop for Android devices (Android SDK version 32.1.0-rc1)
[✓] Xcode - develop for iOS and macOS (Xcode 14.0)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2021.1)
[✓] VS Code (version 1.69.2)
[✓] Connected device (2 available)
[✓] HTTP Host Availability

• No issues found!

解决方法

问题核心是Flutter Web环境下,universal_io的File类无法处理blob:协议路径,Supabase Flutter SDK在Web端需要直接使用浏览器原生的File对象(而非dart:io/Universal IO的File)。

1. 获取浏览器原生File对象

如果是通过文件选择器获取图片,直接使用dart:html包的File对象:

import 'dart:html' as html;

Future<void> pickAndUpload() async {
  final input = html.FileUploadInputElement()..accept = 'image/*';
  input.click();
  await input.onChange.first;
  final file = input.files?.first;
  if (file != null) {
    await uploadToSupabase(file, file.name);
  }
}

如果你的photoFilePath是blob:链接,先将其转换为原生File:

import 'dart:html' as html;

Future<html.File> blobUrlToFile(String blobUrl, String fileName) async {
  final response = await html.HttpRequest.request(blobUrl, responseType: 'blob');
  final blob = response.response as html.Blob;
  return html.File([blob], fileName);
}

2. 调用Supabase上传接口

直接传入浏览器原生File对象到上传方法:

Future<void> uploadToSupabase(html.File file, String fileName) async {
  try {
    final result = await supabase.storage.from('avatars').upload(
      fileName,
      file,
      fileOptions: const FileOptions(contentType: 'image/jpeg'), // 根据文件类型调整
    );
    print('上传成功:${result.data?.path}');
  } catch (e) {
    print('上传失败:$e');
  }
}

3. 跨平台兼容(可选)

如果需要同时支持移动端和Web端,通过条件导入区分实现:

import 'dart:io' show Platform;
import 'package:flutter/foundation.dart' show kIsWeb;

// 定义抽象服务类
abstract class ImageUploader {
  Future<void> uploadImage();
}

// 移动端实现
import 'dart:io';
import 'package:image_picker/image_picker.dart';

class MobileImageUploader implements ImageUploader {
  @override
  Future<void> uploadImage() async {
    final picker = ImagePicker();
    final image = await picker.pickImage(source: ImageSource.gallery);
    if (image != null) {
      await supabase.storage.from('avatars').upload(image.name, File(image.path));
    }
  }
}

// Web端实现
import 'dart:html' as html;

class WebImageUploader implements ImageUploader {
  @override
  Future<void> uploadImage() async {
    final input = html.FileUploadInputElement()..accept = 'image/*';
    input.click();
    await input.onChange.first;
    final file = input.files?.first;
    if (file != null) {
      await supabase.storage.from('avatars').upload(file.name, file);
    }
  }
}

// 使用时选择对应实现
final uploader = kIsWeb ? WebImageUploader() : MobileImageUploader();

内容的提问来源于stack exchange,提问作者bilalkubilay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 16:15:37