fix web save

This commit is contained in:
2025-07-22 21:54:09 +08:00
parent 4859d0cf7e
commit ca5a552371
8 changed files with 74 additions and 14 deletions

View File

@@ -8,6 +8,7 @@ import 'dart:io' show Platform , File, Directory;
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:file_saver/file_saver.dart';
import '../../core/book_downloader.dart';
import '../../models/book.dart';
@@ -58,7 +59,9 @@ class _BookDetailViewState extends ConsumerState<BookDetailView> {
}
}
} catch (err) {
print("获取下载文件夹路径失败: $err");
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('无法获取下载目录')),
);
}
return directory?.path;
}
@@ -70,7 +73,30 @@ class _BookDetailViewState extends ConsumerState<BookDetailView> {
final bool isIOS = !kIsWeb && Platform.isIOS;
try {
if (isAndroid || isIOS) {
if (kIsWeb) {
// --- Web 平台逻辑 ---
if (_selectedFormat == DownloadFormat.chapterTxt) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('不支持分章节下载'),
content: const Text('Web 平台不支持分章节下载,请选择单文件下载。'),
actions: [
TextButton(
child: const Text('确定'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
);
return;
} else {
// 获取一个虚拟的、在内存中的目录路径
final Directory tempDir = await getApplicationDocumentsDirectory();
String extension = _selectedFormat == DownloadFormat.singleTxt ? 'txt' : 'epub';
outputPath = '${tempDir.path}/$fileName.$extension';
}
} else if (isAndroid || isIOS) {
// --- 移动平台 (Android/iOS) 逻辑 ---
var hasPermission = true;
if (isAndroid) {
@@ -154,21 +180,35 @@ class _BookDetailViewState extends ConsumerState<BookDetailView> {
// --- 解决方案: 创建一个局部 final 变量来捕获当前路径的值 ---
final String path = _lastDownloadedPath!;
final String fileName = p.basename(path); // 从完整路径中提取文件名
// 显示 SnackBar
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
// 使用局部变量 'path'
content: Text('下载完成: $path'),
action: _selectedFormat != DownloadFormat.chapterTxt
? SnackBarAction(
label: '打开',
// 回调函数现在捕获的是局部变量 'path',它的值不会被改变
onPressed: () => OpenFile.open(path),
if (kIsWeb) {
// --- Web 平台: 读取虚拟文件并触发浏览器下载 ---
final File file = File(path);
file.readAsBytes().then((bytes) {
// 使用 file_saver 保存文件
FileSaver.instance.saveFile(
name: p.basenameWithoutExtension(fileName), // 文件名 (无扩展名)
bytes: bytes, // 文件内容
fileExtension: p.extension(fileName).replaceFirst('.', ''), // 扩展名 (去掉点)
mimeType: MimeType.text
);
});
} else {
// --- 移动/桌面平台: 显示带“打开”按钮的 SnackBar ---
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('下载完成: $path'),
action: _selectedFormat != DownloadFormat.chapterTxt
? SnackBarAction(
label: '打开',
onPressed: () => OpenFile.open(path),
)
: null,
)
: null,
)
);
);
}
// 现在可以安全地清空成员变量,为下一次下载做准备了
_lastDownloadedPath = null;
}

View File

@@ -6,11 +6,15 @@
#include "generated_plugin_registrant.h"
#include <file_saver/file_saver_plugin.h>
#include <open_file_linux/open_file_linux_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
#include <window_size/window_size_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_saver_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSaverPlugin");
file_saver_plugin_register_with_registrar(file_saver_registrar);
g_autoptr(FlPluginRegistrar) open_file_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "OpenFileLinuxPlugin");
open_file_linux_plugin_register_with_registrar(open_file_linux_registrar);

View File

@@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
file_saver
open_file_linux
url_launcher_linux
window_size

View File

@@ -6,6 +6,7 @@ import FlutterMacOS
import Foundation
import file_picker
import file_saver
import open_file_mac
import package_info_plus
import path_provider_foundation
@@ -14,6 +15,7 @@ import window_size
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
FileSaverPlugin.register(with: registry.registrar(forPlugin: "FileSaverPlugin"))
OpenFilePlugin.register(with: registry.registrar(forPlugin: "OpenFilePlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))

View File

@@ -169,6 +169,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "10.2.0"
file_saver:
dependency: "direct main"
description:
name: file_saver
sha256: "9d93db09bd4da9e43238f9dd485360fc51a5c138eea5ef5f407ec56e58079ac0"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.3.1"
flutter:
dependency: "direct main"
description: flutter

View File

@@ -48,6 +48,7 @@ dependencies:
# 文件与权限
path_provider: ^2.1.1 # 获取设备文件系统路径
file_picker: ^10.2.0 # 允许用户选择保存位置
file_saver: ^0.3.1 # 用于在web端保存文件
permission_handler: ^12.0.1 # 处理存储权限 (尤其在 Android)
open_file: ^3.5.10

View File

@@ -6,11 +6,14 @@
#include "generated_plugin_registrant.h"
#include <file_saver/file_saver_plugin.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
#include <window_size/window_size_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
FileSaverPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSaverPlugin"));
PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
UrlLauncherWindowsRegisterWithRegistrar(

View File

@@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
file_saver
permission_handler_windows
url_launcher_windows
window_size