Flutter文件上传插件files_uploader的使用

发布于 1周前 作者 gougou168 来自 Flutter

Flutter文件上传插件files_uploader的使用

插件简介

files_uploader 是一个用于创建和管理文件上传任务的Flutter插件,支持iOS和Android平台。该插件基于Android的WorkManager和iOS的NSURLSessionUploadTask来实现后台模式下的文件上传。

平台集成

iOS集成

启用后台模式

需要在Xcode中启用应用程序的后台模式,并勾选"Background fetch"和"Remote notifications"选项。

AppDelegate调整

为了使插件能够在后台隔离运行,你需要对AppDelegate.swift进行如下调整:

import files_uploader

func registerPlugins(registry: FlutterPluginRegistry) {
    GeneratedPluginRegistrant.register(with: registry)
}

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)

    SwiftFlutterUploaderPlugin.registerPlugins = registerPlugins

    // Any further code.
  }
}

可选配置

  • 最大连接数:可以在Info.plist中添加键值对来设置每个主机的最大并发连接数。
  • 最大并发上传操作数:同样通过修改Info.plist来限制最大并发上传数量。
  • 请求超时时间:控制上传请求等待数据的时间长度。

Android集成

可选配置

  • 最大并发任务数:可以通过修改AndroidManifest.xml中的元数据来设定最大并发任务数。
  • 连接超时时间:同样在AndroidManifest.xml中设置上传HTTP请求的连接超时时间。

使用示例

导入包

首先,在你的Dart代码中导入files_uploader包:

import 'package:files_uploader/files_uploader.dart';

配置后台隔离入口点

定义一个顶级函数作为后台隔离的入口点,以便监听上传进度和结果:

void backgroundHandler() {
  WidgetsFlutterBinding.ensureInitialized();
  FlutterUploader uploader = FlutterUploader();
  
  uploader.progress.listen((progress) {
    // 处理上传进度
  });
  uploader.result.listen((result) {
    // 处理上传结果
  });
}

// 在应用启动时配置后台处理器
FlutterUploader().setBackgroundHandler(backgroundHandler);

创建新的上传任务

你可以根据需求选择使用MultipartFormDataUploadRawUpload来创建上传任务:

// multipart/form-data上传
final taskId = await FlutterUploader().enqueue(
  MultipartFormDataUpload(
    url: "your upload link",
    files: [FileItem(path: '/path/to/file', fieldname:"file")],
    method: UploadMethod.POST,
    headers: {"apikey": "api_123456", "userkey": "userkey_123456"},
    data: {"name": "john"},
    tag: 'my tag',
  ),
);

// 或者二进制上传
final taskId = await FlutterUploader().enqueue(
  RawUpload(
    url: "your upload link",
    path: '/path/to/file',
    method: UploadMethod.POST,
    headers: {"apikey": "api_123456", "userkey": "userkey_123456"},
    tag: 'my tag',
  ),
);

监听上传进度与结果

你还可以监听上传过程中的进度变化以及最终的结果:

final subscription = FlutterUploader().progress.listen((progress) {
  // 处理进度信息
});

final resultSubscription = FlutterUploader().result.listen((result) {
  // 处理上传结果
}, onError: (ex, stacktrace) {
  // 错误处理
});

取消上传任务

如果需要取消某个特定的任务或者所有正在执行的任务,可以调用以下方法:

// 取消单个任务
FlutterUploader().cancel(taskId: taskId);

// 取消所有任务
FlutterUploader().cancelAll();

示例项目结构

为了更好地理解如何将这些功能整合到实际项目中,请参考完整的main.dart文件内容。这个例子展示了如何结合SharedPreferences存储已处理过的上传ID,并利用flutter_local_notifications显示上传状态的通知。

// 忽略一些lint规则以简化代码展示
// ignore_for_file: public_member_api_docs
// ignore_for_file: avoid_print

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:files_uploader/files_uploader.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';

const String title = 'FileUpload Sample app';
final Uri uploadURL = Uri.parse('https://us-central1-flutteruploadertest.cloudfunctions.net/upload');

FlutterUploader _uploader = FlutterUploader();

void backgroundHandler() {
  WidgetsFlutterBinding.ensureInitialized();
  var uploader = FlutterUploader();
  var notifications = FlutterLocalNotificationsPlugin();

  SharedPreferences.getInstance().then((preferences) {
    var processed = preferences.getStringList('processed') ?? <String>[];

    if (Platform.isAndroid) {
      uploader.progress.listen((progress) {
        if (!processed.contains(progress.taskId)) {
          notifications.show(
            progress.taskId.hashCode,
            'FlutterUploader Example',
            'Upload in Progress',
            NotificationDetails(
              android: AndroidNotificationDetails(
                'FlutterUploader.Example',
                'FlutterUploader',
                channelDescription: 'Installed when you activate the Flutter Uploader Example',
                progress: progress.progress ?? 0,
                icon: 'ic_upload',
                enableVibration: false,
                importance: Importance.low,
                showProgress: true,
                onlyAlertOnce: true,
                maxProgress: 100,
                channelShowBadge: false,
              ),
              iOS: const IOSNotificationDetails(),
            ),
          );
        }
      });
    }

    uploader.result.listen((result) {
      if (!processed.contains(result.taskId)) {
        processed.add(result.taskId);
        preferences.setStringList('processed', processed);

        notifications.cancel(result.taskId.hashCode);

        final successful = result.status == UploadTaskStatus.complete;

        var title = 'Upload Complete';
        if (result.status == UploadTaskStatus.failed) {
          title = 'Upload Failed';
        } else if (result.status == UploadTaskStatus.canceled) {
          title = 'Upload Canceled';
        }

        notifications.show(
          result.taskId.hashCode,
          'FlutterUploader Example',
          title,
          NotificationDetails(
            android: AndroidNotificationDetails(
              'FlutterUploader.Example',
              'FlutterUploader',
              channelDescription: 'Installed when you activate the Flutter Uploader Example',
              icon: 'ic_upload',
              enableVibration: !successful,
              importance: result.status == UploadTaskStatus.failed ? Importance.high : Importance.min,
            ),
            iOS: const IOSNotificationDetails(presentAlert: true),
          ),
        ).catchError((e, stack) {
          print('error while showing notification: $e, $stack');
        });
      }
    });
  });
}

void main() => runApp(const App());

class App extends StatefulWidget {
  const App({Key? key}) : super(key: key);

  @override
  _AppState createState() => _AppState();
}

class _AppState extends State<App> {
  int _currentIndex = 0;
  bool allowCellular = true;

  @override
  void initState() {
    super.initState();
    _uploader.setBackgroundHandler(backgroundHandler);

    var flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
    var initializationSettingsAndroid = const AndroidInitializationSettings('ic_upload');
    var initializationSettingsIOS = IOSInitializationSettings(
      requestSoundPermission: false,
      requestBadgePermission: false,
      requestAlertPermission: true,
      onDidReceiveLocalNotification: (int id, String? title, String? body, String? payload) async {},
    );
    var initializationSettings = InitializationSettings(
      android: initializationSettingsAndroid, 
      iOS: initializationSettingsIOS
    );
    flutterLocalNotificationsPlugin.initialize(
      initializationSettings,
      onSelectNotification: (payload) async {},
    );

    SharedPreferences.getInstance()
        .then((sp) => sp.getBool('allowCellular') ?? true)
        .then((result) {
      if (mounted) {
        setState(() {
          allowCellular = result;
        });
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: title,
      theme: ThemeData(primarySwatch: Colors.blue),
      home: Scaffold(
        appBar: AppBar(
          actions: [
            IconButton(
              icon: Icon(allowCellular
                  ? Icons.signal_cellular_connected_no_internet_4_bar
                  : Icons.wifi_outlined),
              onPressed: () async {
                final sp = await SharedPreferences.getInstance();
                await sp.setBool('allowCellular', !allowCellular);
                if (mounted) {
                  setState(() {
                    allowCellular = !allowCellular;
                  });
                }
              },
            ),
          ],
        ),
        body: _currentIndex == 0
            ? UploadScreen(
                uploader: _uploader,
                uploadURL: uploadURL,
                onUploadStarted: () {
                  setState(() => _currentIndex = 1);
                },
              )
            : ResponsesScreen(
                uploader: _uploader,
              ),
        bottomNavigationBar: BottomNavigationBar(
          items: const <BottomNavigationBarItem>[
            BottomNavigationBarItem(icon: Icon(Icons.cloud_upload), label: 'Upload'),
            BottomNavigationBarItem(icon: Icon(Icons.receipt), label: 'Responses'),
          ],
          onTap: (newIndex) {
            setState(() => _currentIndex = newIndex);
          },
          currentIndex: _currentIndex,
        ),
      ),
    );
  }
}

以上是关于files_uploader插件的基本介绍及其具体使用的详细说明。希望这能帮助你在自己的Flutter项目中实现高效的文件上传功能!


更多关于Flutter文件上传插件files_uploader的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter文件上传插件files_uploader的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter项目中使用files_uploader插件进行文件上传的示例代码。这个插件允许你从设备中选择文件并将其上传到服务器。

首先,确保你已经在pubspec.yaml文件中添加了files_uploader依赖:

dependencies:
  flutter:
    sdk: flutter
  files_uploader: ^x.y.z  # 请替换为最新版本号

然后运行flutter pub get来安装依赖。

接下来,你需要创建一个Flutter应用并集成文件上传功能。以下是一个简单的示例,展示如何使用files_uploader插件:

import 'package:flutter/material.dart';
import 'package:files_uploader/files_uploader.dart';
import 'dart:io';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'File Uploader Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: FileUploadPage(),
    );
  }
}

class FileUploadPage extends StatefulWidget {
  @override
  _FileUploadPageState createState() => _FileUploadPageState();
}

class _FileUploadPageState extends State<FileUploadPage> {
  final FilePicker _picker = FilePicker();
  File? _selectedFile;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('File Uploader Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              _selectedFile == null ? 'No file selected.' : 'File selected: ${_selectedFile!.path}',
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () async {
                // Open file picker
                FilePickerResult? result = await _picker.pickFiles(
                  type: FileType.any,
                );

                if (result != null) {
                  if (result.files.isNotEmpty) {
                    // Get the first file from the picked files
                    File file = File(result.files.first.path!);

                    setState(() {
                      _selectedFile = file;
                    });

                    // Upload the file
                    uploadFile(file);
                  }
                }
              },
              child: Text('Select File'),
            ),
          ],
        ),
      ),
    );
  }

  Future<void> uploadFile(File file) async {
    String url = 'https://your-server-url/upload';  // 替换为你的服务器上传URL
    var request = http.MultipartRequest('POST', Uri.parse(url));

    var multipartFile = await http.MultipartFile.fromPath('file', file.path);
    request.files.add(multipartFile);

    var response = await request.send();
    var responseData = await response.stream.toBytes();
    String responseBody = String.fromCharCodes(responseData);

    print('Upload response: $responseBody');

    // Here you can handle the server response, e.g., show a snackbar or dialog
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text('File uploaded successfully!'),
      ),
    );
  }
}

在这个示例中,我们做了以下几件事:

  1. 使用FilePicker来选择文件。
  2. 显示选中的文件路径。
  3. 使用http.MultipartRequest来上传文件到服务器。

注意事项:

  • 你需要在你的pubspec.yaml文件中添加http依赖,因为上传文件需要用到它。
  • 确保你替换了服务器URL为你的实际上传URL。
  • 这个示例假设服务器期望一个名为file的文件字段。如果你的服务器期望不同的字段名,请相应地修改代码。
dependencies:
  http: ^0.13.3  # 请替换为最新版本号

这个示例提供了一个基本的文件上传流程,你可以根据需要进一步扩展和自定义。

回到顶部