Skip to content
Toggle navigation
Toggle navigation
This project
Loading...
Sign in
ethan
/
appframe
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Network
Compare
Branches
Tags
Commit ba18c03b
authored
2025-10-20 09:36:06 +0800
by
tanghuan
Browse Files
Options
Browse Files
Tag
Download
Email Patches
Plain Diff
dev
1 parent
9c259642
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
220 additions
and
237 deletions
assets/test2.html
lib/bloc/link_cubit.dart
lib/bloc/web_cubit.dart
lib/bloc/wechat_auth_cubit.dart
lib/config/constant.dart
lib/config/locator.dart
lib/config/routes.dart
lib/data/repositories/message/choose_file_handler.dart
lib/data/repositories/message/choose_image_handler.dart
lib/data/repositories/message/crop_image_handler.dart
lib/data/repositories/message/open_document_handler.dart
lib/data/repositories/message/open_link_handler.dart
lib/data/repositories/message/upload_file.dart
lib/services/local_server_service.dart
lib/ui/pages/link_page.dart
lib/ui/pages/web_page.dart
assets/test2.html
View file @
ba18c03
...
...
@@ -9,6 +9,8 @@
<div
id=
"resp"
></div>
<input
type=
"text"
id=
"text"
value=
""
>
<button
onclick=
"startRecord()"
>
开始录音
</button>
<button
onclick=
""
>
暂停录音
</button>
...
...
@@ -23,6 +25,8 @@
<button
onclick=
""
>
唤醒播放
</button>
<button
onclick=
"stopPlay()"
>
停止播放
</button>
<button
onclick=
"openlink()"
>
打开新链接
</button>
<br>
<br>
<a
href=
"/test/test.html"
>
跳转测试1
</a>
...
...
@@ -62,6 +66,12 @@
xeJsBridge
.
postMessage
(
message
);
}
function
openlink
()
{
let
url
=
'https://xw.qq.com'
;
let
message
=
'{ "timestamp": 1, "unique": "123", "cmd": "openlink", "params": {"url":"'
+
url
+
'"} }'
;
xeJsBridge
.
postMessage
(
message
);
}
</script>
</html>
\ No newline at end of file
lib/bloc/link_cubit.dart
0 → 100644
View file @
ba18c03
import
'package:appframe/config/routes.dart'
;
import
'package:equatable/equatable.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:go_router/go_router.dart'
;
import
'package:webview_flutter/webview_flutter.dart'
;
class
LinkState
extends
Equatable
{
final
bool
loaded
;
final
String
url
;
const
LinkState
({
this
.
loaded
=
false
,
this
.
url
=
''
});
LinkState
copyWith
({
bool
?
loaded
,
String
?
url
})
{
return
LinkState
(
loaded:
loaded
??
this
.
loaded
,
url:
url
??
this
.
url
);
}
@override
// TODO: implement props
List
<
Object
?>
get
props
=>
[
loaded
,
url
];
}
class
LinkCubit
extends
Cubit
<
LinkState
>
{
late
final
WebViewController
_controller
;
WebViewController
get
controller
=>
_controller
;
LinkCubit
(
super
.
initialState
)
{
_controller
=
WebViewController
()
..
setJavaScriptMode
(
JavaScriptMode
.
unrestricted
)
..
setNavigationDelegate
(
NavigationDelegate
(
onUrlChange:
(
UrlChange
url
)
{},
onPageStarted:
(
String
url
)
{},
onPageFinished:
(
String
url
)
{
_controller
.
runJavaScript
(
'document.querySelector("meta[name=viewport]").setAttribute("content", "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no")'
,
);
_finishLoading
();
},
),
)
..
loadRequest
(
Uri
.
parse
(
state
.
url
));
}
void
_finishLoading
()
{
emit
(
state
.
copyWith
(
loaded:
true
));
}
Future
<
void
>
handleBack
(
BuildContext
context
)
async
{
if
(
await
_controller
.
canGoBack
())
{
_controller
.
goBack
();
}
else
{
// context.pop(true);
router
.
pop
(
'ok'
);
}
}
}
lib/bloc/web_cubit.dart
View file @
ba18c03
...
...
@@ -197,7 +197,7 @@ class WebCubit extends Cubit<WebState> {
// closeLocalPlayer();
},
onPageFinished:
(
String
url
)
async
{
controller
.
runJavaScript
(
_
controller
.
runJavaScript
(
'document.querySelector("meta[name=viewport]").setAttribute("content", "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no")'
,
);
finishLoading
();
...
...
@@ -225,8 +225,8 @@ class WebCubit extends Cubit<WebState> {
final
String
serverUrl
;
if
(
state
.
sessionCode
==
null
||
state
.
sessionCode
==
''
)
{
serverUrl
=
'
http://127.0.0.1:
${_server.port}
/test
/login.html'
;
// serverUrl = '
http://127.0.0.1:${_server.port}/test/test
.html';
serverUrl
=
'
${Constant.localServerTestFileUrl}
/login.html'
;
// serverUrl = '
${Constant.localServerTestFileUrl}/test2
.html';
}
else
{
serverUrl
=
'http://
${state.ip}
:
${_server.port}
/index.html#/h5/login/pages/applogin?sessionCode=
${state.sessionCode}
&userCode=
${state.userCode}
&classCode=
${state.classCode}
&userType=
${state.userType}
&stuId=
${state.stuId}
'
;
...
...
@@ -270,7 +270,7 @@ class WebCubit extends Cubit<WebState> {
//测试
void
goAuth
()
{
// String serverUrl = 'http://${state.ip}:${_server.port}/index.html';
String
serverUrl
=
'
http://127.0.0.1:
${_server.port
}
/index.html'
;
String
serverUrl
=
'
${Constant.localServerUrl
}
/index.html'
;
// String serverUrl = 'http://localdev.banxiaoer.net';
_controller
.
loadRequest
(
Uri
.
parse
(
serverUrl
));
}
...
...
@@ -423,11 +423,11 @@ class WebCubit extends Cubit<WebState> {
).
writeAsBytes
(
data
!);
return
{
"tempFilePath"
:
'
http://127.0.0.1:
${_server.port}
/temp
${file!.path}
'
,
"tempFilePath"
:
'
${Constant.localServerTempFileUrl}
${file!.path}
'
,
"size"
:
file
.
lengthSync
(),
"width"
:
asset
.
width
,
"height"
:
asset
.
height
,
"thumbTempFilePath"
:
'
http://127.0.0.1:
${_server.port}
/temp
${thumbnailFile.path}
'
,
"thumbTempFilePath"
:
'
${Constant.localServerTempFileUrl}
${thumbnailFile.path}
'
,
"fileType"
:
file
.
path
.
split
(
'/'
).
last
.
split
(
'.'
).
last
,
};
}
...
...
@@ -545,11 +545,11 @@ class WebCubit extends Cubit<WebState> {
).
writeAsBytes
(
data
!);
return
{
"tempFilePath"
:
'
http://127.0.0.1:
${_server.port}
/temp
${file!.path}
'
,
"tempFilePath"
:
'
${Constant.localServerTempFileUrl}
${file!.path}
'
,
"size"
:
file
.
lengthSync
(),
"width"
:
asset
.
width
,
"height"
:
asset
.
height
,
"thumbTempFilePath"
:
'
http://127.0.0.1:
${_server.port}
/temp
${thumbnailFile.path}
'
,
"thumbTempFilePath"
:
'
${Constant.localServerTempFileUrl}
${thumbnailFile.path}
'
,
"fileType"
:
file
.
path
.
split
(
'/'
).
last
.
split
(
'.'
).
last
,
};
}
...
...
@@ -732,10 +732,7 @@ class WebCubit extends Cubit<WebState> {
// var duration = await AudioUtil.getAudioDuration(mp3Path);
var
duration
=
await
AudioUtil
.
getAudioDuration
(
url
);
return
{
'tempFilePath'
:
'http://127.0.0.1:
${Constant.localServerPort}
/temp
$mp3Path
'
,
'duration'
:
duration
.
inSeconds
,
};
return
{
'tempFilePath'
:
'
${Constant.localServerTempFileUrl}$mp3Path
'
,
'duration'
:
duration
.
inSeconds
};
}
/// 清空录音
...
...
lib/bloc/wechat_auth_cubit.dart
View file @
ba18c03
...
...
@@ -19,7 +19,7 @@ class WechatAuthState extends Equatable {
final
String
?
stuId
;
const
WechatAuthState
({
this
.
ip
=
'127.0.0.1'
,
this
.
ip
=
Constant
.
localServerHost
,
this
.
result
,
this
.
sessionCode
,
this
.
userCode
,
...
...
lib/config/constant.dart
View file @
ba18c03
class
Constant
{
static
const
String
localServerHost
=
'127.0.0.1'
;
/// 应用内部 http 服务
static
const
int
localServerPort
=
35982
;
static
const
String
localServerHost
=
'127.0.0.1'
;
static
const
String
localServerUrl
=
'http://
$localServerHost
:
$localServerPort
'
;
static
const
String
localServerTemp
=
'/temp'
;
static
const
String
localServerTempFileUrl
=
'
$localServerUrl$localServerTemp
'
;
static
const
String
localServerTest
=
'/test'
;
static
const
String
localServerTestFileUrl
=
'
$localServerUrl$localServerTest
'
;
/// obs文件分片上传的分片大小:5M
static
const
int
obsUploadChunkSize
=
1024
*
1024
*
5
;
// static const String h5Server = 'appdev-xj.banxiaoer.net';
static
const
String
h5Server
=
'appdev-th.banxiaoer.net'
;
/// 测试阶段使用的 h5 服务地址
static
const
String
h5Server
=
'appdev-xj.banxiaoer.net'
;
// static const String h5Server = 'appdev-th.banxiaoer.net';
// static const String h5Server = '192.168.1.136';
}
lib/config/locator.dart
View file @
ba18c03
...
...
@@ -14,6 +14,7 @@ import 'package:appframe/data/repositories/message/image_info_handler.dart';
import
'package:appframe/data/repositories/message/location_handler.dart'
;
import
'package:appframe/data/repositories/message/network_type_handler.dart'
;
import
'package:appframe/data/repositories/message/open_document_handler.dart'
;
import
'package:appframe/data/repositories/message/open_link_handler.dart'
;
import
'package:appframe/data/repositories/message/open_weapp_handler.dart'
;
import
'package:appframe/data/repositories/message/orientation_handler.dart'
;
import
'package:appframe/data/repositories/message/save_file_to_disk_handler.dart'
;
...
...
@@ -159,6 +160,9 @@ Future<void> setupLocator() async {
/// 设置标题和返回按钮
getIt
.
registerLazySingleton
<
MessageHandler
>(()
=>
SetTitleHandler
(),
instanceName:
'setTitle'
);
/// 新路由打开链接
getIt
.
registerLazySingleton
<
MessageHandler
>(()
=>
OpenLinkHandler
(),
instanceName:
'openlink'
);
/// 登录
getIt
.
registerLazySingleton
<
MessageHandler
>(()
=>
GoLoginHandler
(),
instanceName:
'goLogin'
);
...
...
lib/config/routes.dart
View file @
ba18c03
import
'package:appframe/ui/pages/link_page.dart'
;
import
'package:appframe/ui/pages/scan_code_page.dart'
;
import
'package:appframe/ui/pages/web_page.dart'
;
import
'package:appframe/ui/pages/wechat_auth_page.dart'
;
...
...
@@ -25,5 +26,11 @@ final GoRouter router = GoRouter(
return
const
ScanCodePage
();
},
),
GoRoute
(
path:
'/link'
,
builder:
(
BuildContext
context
,
GoRouterState
state
)
{
return
const
LinkPage
();
},
),
],
);
lib/data/repositories/message/choose_file_handler.dart
View file @
ba18c03
...
...
@@ -87,13 +87,13 @@ class ChooseFileHandler extends MessageHandler {
// 返回临时文件信息
return
{
'tempFilePath'
:
'
http://127.0.0.1:
${Constant.localServerPort}
/temp
${file.path}
'
,
'tempFilePath'
:
'
${Constant.localServerTempFileUrl}
${file.path}
'
,
'size'
:
file
.
size
,
'width'
:
isImage
?
imgWidth
:
(
isVideo
?
videoWidth
:
''
),
'height'
:
isImage
?
imgHeight
:
(
isVideo
?
videoHeight
:
''
),
'thumbTempFilePath'
:
isImage
?
'
http://127.0.0.1:
${Constant.localServerPort}
/temp
$imgThumbFilePath
'
:
(
isVideo
?
'
http://127.0.0.1:
${Constant.localServerPort}
/temp
$videoThumbFilePath
'
:
''
),
?
'
${Constant.localServerTempFileUrl}
$imgThumbFilePath
'
:
(
isVideo
?
'
${Constant.localServerTempFileUrl}
$videoThumbFilePath
'
:
''
),
'fileType'
:
file
.
extension
,
};
}
...
...
lib/data/repositories/message/choose_image_handler.dart
View file @
ba18c03
...
...
@@ -48,189 +48,3 @@ class ChooseImageHandler extends MessageHandler {
_webCubit
!.
setChooseImageCmdFlag
(
true
,
_message
!);
}
}
// import 'dart:io';
//
// import 'package:appframe/services/dispatcher.dart';
// import 'package:flutter_image_compress/flutter_image_compress.dart';
// import 'package:image_picker/image_picker.dart';
// import 'package:image_size_getter/file_input.dart';
// import 'package:image_size_getter/image_size_getter.dart';
// import 'package:path/path.dart' as path;
// import 'package:path_provider/path_provider.dart';
//
// class ChooseImageHandler extends MessageHandler {
// @override
// Future<dynamic> handleMessage(dynamic params) async {
// if (params is! Map<String, dynamic>) {
// throw Exception('参数错误');
// }
// var sourceType = params['sourceType'] as String;
// if (sourceType != 'album' && sourceType != 'camera') {
// throw Exception('参数错误');
// }
// // 暂时忽略对此参数的处理
// List<dynamic>? sizeType;
// if (params.containsKey('sizeType')) {
// sizeType = params['sizeType'] as List<dynamic>;
// if (sizeType.isEmpty || sizeType.length > 2) {
// throw Exception('参数错误');
// }
// }
//
// int count = 9;
// if (params.containsKey('count')) {
// count = params['count'] as int;
// if (count < 1 || count > 9) {
// throw Exception('参数错误');
// }
// }
//
// // 从相册选择
// if (sourceType == 'album') {
// if (count == 1) {
// return await _selectSingle();
// } else {
// return await _selectMulti(count);
// }
// }
// // 拍照
// else {
// return await _cameraSingle();
// }
// }
//
// ///
// /// 选择单张图片
// ///
// /// 将选择的图片放到应用的临时目录中,并在路径前面添加“/temp”,返回给调用方后,调用方可通过http方式访问图片
// ///
// Future<List<Map<String, dynamic>>?> _selectSingle() async {
// final ImagePicker picker = ImagePicker();
//
// final XFile? pickedFile = await picker.pickImage(source: ImageSource.gallery);
//
// // 用户取消选择,返回空数组
// if (pickedFile == null) {
// return [];
// }
//
// // 获取临时目录
// final Directory tempDir = await getTemporaryDirectory();
//
// return [await _handleOne(pickedFile, tempDir)];
// }
//
// ///
// /// 选择多张图片
// ///
// /// 将选择的图片放到应用的临时目录中,并在每个文件路径前面添加“/temp”,返回给调用方后,调用方可通过http方式访问图片
// ///
// Future<List<Map<String, dynamic>>?> _selectMulti(int limit) async {
// final ImagePicker picker = ImagePicker();
//
// final List<XFile> pickedFileList = await picker.pickMultiImage(limit: limit);
//
// // 用户取消选择,返回空数组
// if (pickedFileList.isEmpty) {
// return [];
// }
//
// // 限制最多limit张
// if (pickedFileList.length > limit) {
// pickedFileList.removeRange(limit, pickedFileList.length);
// }
//
// // 获取临时目录
// final Directory tempDir = await getTemporaryDirectory();
//
// final List<Map<String, dynamic>> result = [];
// for (final XFile? file in pickedFileList) {
// if (file != null) {
// result.add(await _handleOne(file, tempDir));
// }
// }
//
// return result;
// }
//
// ///
// /// 拍照
// ///
// Future<List<Map<String, dynamic>>?> _cameraSingle() async {
// final ImagePicker picker = ImagePicker();
//
// final XFile? pickedFile = await picker.pickImage(source: ImageSource.camera);
//
// // 用户取消选择,返回空数组
// if (pickedFile == null) {
// return [];
// }
//
// // 获取临时目录
// final Directory tempDir = await getTemporaryDirectory();
//
// return [await _handleOne(pickedFile, tempDir)];
// }
//
// Future<Map<String, dynamic>> _handleOne3(XFile pickedFile, Directory tempDir) async {
// // 生成唯一文件名
// final String fileName = path.basename(pickedFile.path);
// final String uniqueFileName = '${DateTime.now().millisecondsSinceEpoch}_$fileName';
//
// // 创建目标文件路径
// final String tempFilePath = path.join(tempDir.path, uniqueFileName);
//
// // 复制文件到临时目录
// var sourceFile = File(pickedFile.path);
// final File copiedFile = await sourceFile.copy(tempFilePath);
//
// // 通过image_size_getter获取图片尺寸
// final sizeResult = ImageSizeGetter.getSizeResult(FileInput(sourceFile));
// final thumbnailPath = await _genThumbnail(sourceFile, tempDir);
//
// // 返回一个元素的数组
// return {
// "tempFilePath": "/temp${copiedFile.path}",
// "size": copiedFile.lengthSync(),
// "width": sizeResult.size.width,
// "height": sizeResult.size.height,
// "thumbTempFilePath": '/temp$thumbnailPath',
// "fileType": copiedFile.path.split('/').last.split('.').last,
// };
// }
//
// Future<Map<String, dynamic>> _handleOne(XFile pickedFile, Directory tempDir) async {
// var sourceFile = File(pickedFile.path);
//
// // 通过image_size_getter获取图片尺寸
// final sizeResult = ImageSizeGetter.getSizeResult(FileInput(sourceFile));
// final thumbnailPath = await _genThumbnail(sourceFile, tempDir);
//
// // 返回一个元素的数组
// return {
// "tempFilePath": "/temp${sourceFile.path}",
// "size": sourceFile.lengthSync(),
// "width": sizeResult.size.width,
// "height": sizeResult.size.height,
// "thumbTempFilePath": '/temp$thumbnailPath',
// "fileType": sourceFile.path.split('/').last.split('.').last,
// };
// }
//
// Future<String?> _genThumbnail(File imageFile, Directory tempDir) async {
// try {
// // 缩略图路径
// final tempPath = tempDir.path;
// final targetPath = '$tempPath/thumbnail_${DateTime.now().millisecondsSinceEpoch}.jpg';
//
// // 压缩生成缩略图文件
// final compressedFile = await FlutterImageCompress.compressAndGetFile(imageFile.absolute.path, targetPath);
//
// return compressedFile!.path;
// } catch (e) {
// print('生成缩略图出错: $e');
// return null;
// }
// }
// }
lib/data/repositories/message/crop_image_handler.dart
View file @
ba18c03
...
...
@@ -30,19 +30,19 @@ class CropImageHandler extends MessageHandler {
try
{
String
outputPath
=
await
_cropImageByRatio
(
url
,
cropScale
);
return
{
'tempFilePath'
:
'
http://127.0.0.1:
${Constant.localServerPort}
/temp
$outputPath
'
};
return
{
'tempFilePath'
:
'
${Constant.localServerTempFileUrl}
$outputPath
'
};
}
catch
(
e
)
{
throw
Exception
(
'裁剪出错'
);
}
}
Future
<
String
>
_cropImageByRatio
(
String
url
,
String
cropScale
)
async
{
if
(
url
.
startsWith
(
'http://127.0.0.1:
${Constant.localServerPort}
'
))
{
url
=
url
.
replaceFirst
(
'http://127.0.0.1:
${Constant.localServerPort}
'
,
''
);
if
(
url
.
startsWith
(
Constant
.
localServerUrl
))
{
url
=
url
.
replaceFirst
(
Constant
.
localServerUrl
,
''
);
}
if
(
url
.
startsWith
(
'/temp'
))
{
url
=
url
.
substring
(
5
);
if
(
url
.
startsWith
(
Constant
.
localServerTemp
))
{
url
=
url
.
replaceFirst
(
Constant
.
localServerTemp
,
''
);
}
String
extension
=
path
.
extension
(
url
);
...
...
lib/data/repositories/message/open_document_handler.dart
View file @
ba18c03
...
...
@@ -15,12 +15,12 @@ class OpenDocumentHandler extends MessageHandler {
throw
Exception
(
'参数错误'
);
}
if
(
url
.
startsWith
(
'http://127.0.0.1:
${Constant.localServerPort}
'
))
{
url
=
url
.
replaceFirst
(
'http://127.0.0.1:
${Constant.localServerPort}
'
,
''
);
if
(
url
.
startsWith
(
Constant
.
localServerUrl
))
{
url
=
url
.
replaceFirst
(
Constant
.
localServerUrl
,
''
);
}
if
(
url
.
startsWith
(
'/temp'
))
{
url
=
url
.
substring
(
5
);
if
(
url
.
startsWith
(
Constant
.
localServerTemp
))
{
url
=
url
.
replaceFirst
(
Constant
.
localServerTemp
,
''
);
}
if
(
url
.
startsWith
(
'http'
))
{
...
...
lib/data/repositories/message/open_link_handler.dart
0 → 100644
View file @
ba18c03
import
'package:appframe/config/routes.dart'
;
import
'package:appframe/services/dispatcher.dart'
;
class
OpenLinkHandler
extends
MessageHandler
{
@override
Future
<
bool
>
handleMessage
(
params
)
async
{
if
(
params
is
!
Map
<
String
,
dynamic
>)
{
throw
Exception
(
'参数错误'
);
}
final
String
url
=
params
[
'url'
]
as
String
;
if
(
url
.
isEmpty
)
{
throw
Exception
(
'参数错误'
);
}
return
_openLink
(
url
);
}
bool
_openLink
(
String
url
)
{
router
.
push
(
'/link'
,
extra:
{
'url'
:
url
});
return
true
;
}
}
lib/data/repositories/message/upload_file.dart
View file @
ba18c03
...
...
@@ -47,21 +47,28 @@ class UploadFileHandler extends MessageHandler {
String
busi
=
fileParams
[
'busi'
]
as
String
;
String
subBusi
=
fileParams
[
'subBusi'
]
as
String
;
if
(
filePath
.
startsWith
(
'http://127.0.0.1:
${Constant.localServerPort}
'
))
{
filePath
=
filePath
.
replaceFirst
(
'http://127.0.0.1:
${Constant.localServerPort}
'
,
''
);
print
(
'参数-------'
);
print
(
'filePath:
$filePath
'
);
print
(
'busi:
$busi
'
);
print
(
'subBusi:
$subBusi
'
);
print
(
'参数-------'
);
if
(
filePath
.
startsWith
(
Constant
.
localServerUrl
))
{
filePath
=
filePath
.
replaceFirst
(
Constant
.
localServerUrl
,
''
);
}
if
(
filePath
.
startsWith
(
'/temp'
))
{
filePath
=
filePath
.
substring
(
5
);
if
(
filePath
.
startsWith
(
Constant
.
localServerTemp
))
{
filePath
=
filePath
.
replaceFirst
(
Constant
.
localServerTemp
,
''
);
}
String
logicPrefix
=
_getLoginPrefix
(
busi
,
subBusi
);
final
bxeApiService
=
ApiService
(
baseUrl:
_bxeBaseUrl
);
// 由于服务端签名时未设置Content-Type,这里必须设置为空,否则会报签名错误
// 由于封装有默认值,所以不能不设置
final
obsApiService
=
ApiService
(
defaultHeaders:
{
'Content-Type'
:
''
,
'Accept'
:
''
});
String
logicPrefix
=
_getLoginPrefix
(
busi
,
subBusi
);
print
(
'logicPrefix:
$logicPrefix
'
);
//并行上传分段
final
uploadResult
=
await
_uploadInParallel
(
bxeApiService
,
obsApiService
,
logicPrefix
,
filePath
);
String
objectKey
=
uploadResult
[
'objectKey'
]
as
String
;
...
...
@@ -70,13 +77,14 @@ class UploadFileHandler extends MessageHandler {
Map
<
int
,
String
>
tagsMap
=
uploadResult
[
'tagsMap'
]
as
Map
<
int
,
String
>;
//请求合并文件
Response
response
=
await
_merge
(
bxeApiService
,
objectKey
,
bucket
,
uploadId
,
tagsMap
);
String
location
=
await
_merge
(
bxeApiService
,
objectKey
,
bucket
,
uploadId
,
tagsMap
);
print
(
'location:
$location
'
);
//关闭Dio
bxeApiService
.
close
();
obsApiService
.
close
();
return
{
'url'
:
_addPreUrl
(
response
.
data
[
"location"
]
)};
return
{
'url'
:
_addPreUrl
(
location
)};
}
/// 并行上传
...
...
@@ -98,8 +106,8 @@ class UploadFileHandler extends MessageHandler {
throw
Exception
(
'上传的文件过大'
);
}
//分段大小
5
M
final
chunkSize
=
1024
*
1024
*
5
;
//分段大小
2
M
final
chunkSize
=
Constant
.
obsUploadChunkSize
;
//分段总数
final
totalChunks
=
(
fileSize
/
chunkSize
).
ceil
();
...
...
@@ -119,6 +127,7 @@ class UploadFileHandler extends MessageHandler {
// String objectKey = 'd2/test/file.csv';
var
uuid
=
Uuid
();
String
objectKey
=
'
$logicPrefix
/
${uuid.v4()}${path.extension(file.path)}
'
;
print
(
'objectKey:
$objectKey
'
);
String
uploadId
=
''
;
Map
<
int
,
String
>
tagsMap
=
{};
...
...
@@ -146,6 +155,7 @@ class UploadFileHandler extends MessageHandler {
final
nextResult
=
await
_next
(
bxeApiService
,
objectKey
,
bucket
,
uploadId
,
i
+
1
);
chunkSignUrl
=
nextResult
[
'signed_url'
]
as
String
;
}
print
(
'chunkSignUrl:
$chunkSignUrl
'
);
// await _uploadChunkWithRetry(obsApiService, chunkSignUrl, i, chunk, tagsMap);
final
future
=
_uploadChunkWithRetry
(
obsApiService
,
chunkSignUrl
,
i
,
chunk
,
tagsMap
);
...
...
@@ -224,7 +234,7 @@ class UploadFileHandler extends MessageHandler {
}
/// 请求合并文件
static
Future
<
Response
>
_merge
(
static
Future
<
String
>
_merge
(
ApiService
bxeApiService
,
String
objectKey
,
String
bucket
,
...
...
@@ -247,7 +257,7 @@ class UploadFileHandler extends MessageHandler {
throw
Exception
(
'合并文件失败'
);
}
return
response
;
return
response
.
data
[
"location"
]
;
}
static
String
_getLoginPrefix
(
String
busi
,
String
subBusi
)
{
...
...
@@ -256,18 +266,17 @@ class UploadFileHandler extends MessageHandler {
var
month
=
now
.
month
;
var
day
=
now
.
day
;
return
'
/
d2/pridel/user/
$year$month$day
/bxe/
${busi}
_
$subBusi
'
;
return
'd2/pridel/user/
$year$month$day
/bxe/
${busi}
_
$subBusi
'
;
}
static
String
_addPreUrl
(
String
location
)
{
// /bxe-pics//d2/pridel/user/20251017/bxe/bxe_homework/f4ea233d-9e1b-4a3f-bc8f-b64e776f42a6.jpg
// 多了一个斜杠,暂时多截取1位
// /bxe-pics/d2/pridel/user/20251017/bxe/bxe_homework/f4ea233d-9e1b-4a3f-bc8f-b64e776f42a6.jpg
if
(
location
.
startsWith
(
'/bxe-files'
))
{
return
'https://files-obs.banxiaoer.com
${location.substring(1
1
)}
'
;
return
'https://files-obs.banxiaoer.com
${location.substring(1
0
)}
'
;
}
else
if
(
location
.
startsWith
(
'/bxe-pics'
))
{
return
'https://pics-obs.banxiaoer.com
${location.substring(
10
)}
'
;
return
'https://pics-obs.banxiaoer.com
${location.substring(
9
)}
'
;
}
else
if
(
location
.
startsWith
(
'/bxe-videos'
))
{
return
'https://videos-obs.banxiaoer.com
${location.substring(1
2
)}
'
;
return
'https://videos-obs.banxiaoer.com
${location.substring(1
1
)}
'
;
}
else
{
return
location
;
}
...
...
lib/services/local_server_service.dart
View file @
ba18c03
...
...
@@ -21,10 +21,10 @@ class LocalServerService {
final
String
requestPath
=
request
.
uri
.
path
==
'/'
?
'/index.html'
:
request
.
uri
.
path
;
try
{
if
(
requestPath
.
startsWith
(
'
/temp
/'
))
{
if
(
requestPath
.
startsWith
(
'
${Constant.localServerTemp}
/'
))
{
// 目录文件服务逻辑
await
_serveTempFile
(
request
,
requestPath
);
}
else
if
(
requestPath
.
startsWith
(
'
/test
/'
))
{
}
else
if
(
requestPath
.
startsWith
(
'
${Constant.localServerTest}
/'
))
{
// 内部assets文件服务逻辑
await
_serveAssetFile
(
request
,
requestPath
);
}
else
{
...
...
@@ -48,7 +48,7 @@ class LocalServerService {
try
{
// 临时文件路径
// 构建文件路径(移除 /temp 前缀)
final
String
filePath
=
requestPath
.
substring
(
'/temp/'
.
length
);
final
String
filePath
=
requestPath
.
substring
(
Constant
.
localServerTemp
.
length
);
// 检查文件是否存在
final
File
file
=
File
(
filePath
);
...
...
@@ -108,7 +108,7 @@ class LocalServerService {
// 访问assets目录下的文件
Future
<
void
>
_serveAssetFile
(
HttpRequest
request
,
String
requestPath
)
async
{
// 构建文件路径(移除 /test 前缀)
final
String
path
=
requestPath
.
substring
(
'/test'
.
length
);
final
String
path
=
requestPath
.
substring
(
Constant
.
localServerTest
.
length
);
final
String
filePath
=
'assets
$path
'
;
try
{
...
...
lib/ui/pages/link_page.dart
0 → 100644
View file @
ba18c03
import
'package:appframe/bloc/link_cubit.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:go_router/go_router.dart'
;
import
'package:webview_flutter/webview_flutter.dart'
;
class
LinkPage
extends
StatelessWidget
{
const
LinkPage
({
super
.
key
});
@override
Widget
build
(
BuildContext
buildContext
)
{
final
Map
<
String
,
dynamic
>?
extraData
=
GoRouterState
.
of
(
buildContext
).
extra
as
Map
<
String
,
dynamic
>?;
final
String
?
url
=
extraData
?[
'url'
];
return
BlocProvider
(
create:
(
context
)
=>
LinkCubit
(
LinkState
(
loaded:
false
,
url:
url
!)),
child:
BlocConsumer
<
LinkCubit
,
LinkState
>(
builder:
(
ctx
,
state
)
{
return
PopScope
(
canPop:
false
,
onPopInvokedWithResult:
(
didPop
,
result
)
{
ctx
.
read
<
LinkCubit
>().
handleBack
(
ctx
);
},
child:
Scaffold
(
appBar:
AppBar
(
title:
Text
(
'adv'
),
centerTitle:
true
,
automaticallyImplyLeading:
false
,
leading:
IconButton
(
icon:
const
Icon
(
Icons
.
arrow_back
),
onPressed:
()
async
{
await
ctx
.
read
<
LinkCubit
>().
handleBack
(
ctx
);
},
),
),
body:
state
.
loaded
?
SizedBox
(
height:
MediaQuery
.
of
(
ctx
).
size
.
height
-
120
,
// 减去100像素留空
child:
WebViewWidget
(
controller:
ctx
.
read
<
LinkCubit
>().
controller
),
)
:
const
Center
(
child:
CircularProgressIndicator
()),
),
);
},
listener:
(
context
,
state
)
{},
),
);
}
}
lib/ui/pages/web_page.dart
View file @
ba18c03
import
'package:appframe/bloc/web_cubit.dart'
;
import
'package:appframe/config/constant.dart'
;
import
'package:appframe/config/locator.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
...
...
@@ -13,7 +14,7 @@ class WebPage extends StatelessWidget {
Widget
build
(
BuildContext
buildContext
)
{
final
Map
<
String
,
dynamic
>?
extraData
=
GoRouterState
.
of
(
buildContext
).
extra
as
Map
<
String
,
dynamic
>?;
var
ip
=
extraData
?[
'ip'
]
??
'127.0.0.1'
;
var
ip
=
extraData
?[
'ip'
]
??
Constant
.
localServerHost
;
var
sessionCode
=
extraData
?[
'sessionCode'
];
var
userCode
=
extraData
?[
'userCode'
];
var
classCode
=
extraData
?[
'classCode'
];
...
...
Write
Preview
Styling with
Markdown
is supported
Attach a file
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to post a comment