Impl saveFile on Windows platform

This commit is contained in:
2023-09-09 09:56:45 +08:00
parent 99550bdbda
commit 25447e9667
6 changed files with 90 additions and 5 deletions

View File

@@ -4,6 +4,7 @@ import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart';
import '../api/client.dart';
import '../api/gallery.dart';
import '../globals.dart';
@@ -45,6 +46,7 @@ class Thumbnail extends StatefulWidget {
enum _ThumbnailMenu {
copyImage,
copyImgUrl,
saveAs,
}
class _Thumbnail extends State<Thumbnail> {
@@ -55,6 +57,7 @@ class _Thumbnail extends State<Thumbnail> {
bool _showNsfw = false;
String? _uri;
CancelToken? _cancel;
String? _fileName;
Future<void> _fetchData() async {
try {
_cancel = CancelToken();
@@ -106,6 +109,7 @@ class _Thumbnail extends State<Thumbnail> {
_fileId = widget._fileId;
_showNsfw = false;
_uri = null;
_fileName = "${basenameWithoutExtension(widget._pMeta.name)}_thumb";
super.initState();
}
@@ -133,6 +137,13 @@ class _Thumbnail extends State<Thumbnail> {
_log.warning("Failed to copy image url to clipboard:", err);
}
break;
case _ThumbnailMenu.saveAs:
try {
await platformPath.saveFile(_fileName!, "image/jpeg", _data!);
} catch (err) {
_log.warning("Failed to save image:", err);
}
break;
}
}
@@ -160,6 +171,9 @@ class _Thumbnail extends State<Thumbnail> {
PopupMenuItem(
value: _ThumbnailMenu.copyImgUrl,
child: Text(AppLocalizations.of(context)!.copyImgUrl)),
PopupMenuItem(
value: _ThumbnailMenu.saveAs,
child: Text(AppLocalizations.of(context)!.saveAs)),
];
return list;
}));

View File

@@ -35,5 +35,6 @@
"none": "none",
"sortByGid": "Sort by gallery id",
"asc": "Ascending",
"desc": "Descending"
"desc": "Descending",
"saveAs": "Save As"
}

View File

@@ -35,5 +35,6 @@
"none": "无",
"sortByGid": "按画廊ID排序",
"asc": "升序",
"desc": "降序"
"desc": "降序",
"saveAs": "另存为"
}

View File

@@ -7,7 +7,7 @@ final Logger _log = Logger("platformPath");
class Path {
static const platform = MethodChannel("lifegpc.eh_downloader_flutter/path");
static const _safChannel=MethodChannel("lifegpc.eh_downloader_flutter/saf");
static const _safChannel = MethodChannel("lifegpc.eh_downloader_flutter/saf");
String? _currentExe;
bool _currentExeLoaded = false;
@@ -28,7 +28,10 @@ class Path {
}
/// 保存文件
static Future<void> saveFile(String filenameWithoutExtension,String mimeType,Uint8List bytes,{String dir=""}) async{
return _safChannel.invokeMethod("saveFile",[filenameWithoutExtension,dir,mimeType,bytes]);
Future<void> saveFile(
String filenameWithoutExtension, String mimeType, Uint8List bytes,
{String dir = ""}) async {
return _safChannel.invokeMethod(
"saveFile", [filenameWithoutExtension, dir, mimeType, bytes]);
}
}

View File

@@ -39,6 +39,7 @@ target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib")
target_link_libraries(${BINARY_NAME} PRIVATE utils)
target_link_libraries(${BINARY_NAME} PRIVATE "Comdlg32.lib")
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
# Run the Flutter tool portions of the build. This must not be removed.

View File

@@ -58,6 +58,71 @@ bool FlutterWindow::OnCreate() {
result->NotImplemented();
}
});
flutter::MethodChannel<> saf(flutter_controller_->engine()->messenger(), "lifegpc.eh_downloader_flutter/saf",
&flutter::StandardMethodCodec::GetInstance());
saf.SetMethodCallHandler([&](const flutter::MethodCall<>& call, std::unique_ptr<flutter::MethodResult<>> result) {
if (call.method_name() == "saveFile") {
auto args = std::get_if<flutter::EncodableList>(call.arguments());
auto fileName = std::get_if<std::string>(&args->at(0));
auto dir = std::get_if<std::string>(&args->at(1));
auto mimeType = std::get_if<std::string>(&args->at(2));
auto data = std::get_if<std::vector<uint8_t>>(&args->at(3));
if (!fileName || !dir || !mimeType || !data) {
result->Error("INVALID_ARGUMENT", "Invalid arguments.");
return;
}
std::wstring wFileName;
if (!wchar_util::str_to_wstr(wFileName, *fileName, CP_UTF8)) {
result->Error("ERROR", "Failed to convert file name to wstring.");
return;
}
std::wstring wDir;
if (!dir->empty() && !wchar_util::str_to_wstr(wDir, *dir, CP_UTF8)) {
result->Error("ERROR", "Failed to convert dir to wstring.");
return;
}
OPENFILENAMEW ofn;
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = Win32Window::GetHandle();
std::wstring filter;
std::wstring defExt;
if (*mimeType == "image/jpeg") {
filter.append(std::wstring(L"JPEG File(*.jpg)\0*.jpg\0", 23));
defExt = L"jpg";
} else if (*mimeType == "image/png") {
filter.append(std::wstring(L"PNG File(*.png)\0*.png\0", 22));
defExt = L"png";
} else if (*mimeType == "image/gif") {
filter.append(std::wstring(L"GIF File(*.gif)\0*.gif\0", 22));
defExt = L"gif";
};
filter.append(std::wstring(L"All Files\0*.*\0\0", 15));
ofn.lpstrFilter = filter.c_str();
ofn.lpstrDefExt = defExt.empty() ? nullptr : defExt.c_str();
wchar_t wFileNameBuf[MAX_PATH_SIZE];
memcpy(wFileNameBuf, wFileName.c_str(), (wFileName.size() + 1) * sizeof(wchar_t));
ofn.lpstrFile = wFileNameBuf;
ofn.nMaxFile = MAX_PATH_SIZE;
ofn.lpstrInitialDir = wDir.empty() ? nullptr : wDir.c_str();
ofn.Flags = OFN_DONTADDTORECENT | OFN_NONETWORKBUTTON | OFN_NOREADONLYRETURN | OFN_OVERWRITEPROMPT;
if (!GetSaveFileNameW(&ofn)) {
result->Error("ERROR", "Failed to get file name.");
return;
}
FILE* f = nullptr;
_wfopen_s(&f, wFileNameBuf, L"wb");
if (!f) {
result->Error("ERROR", "Failed to open file.");
return;
}
fwrite(data->data(), sizeof(uint8_t), data->size(), f);
fclose(f);
result->Success();
} else {
result->NotImplemented();
}
});
SetChildContent(flutter_controller_->view()->GetNativeWindow());