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 9c259642
authored
2025-10-17 13:31:30 +0800
by
tanghuan
Browse Files
Options
Browse Files
Tag
Download
Email Patches
Plain Diff
dev
1 parent
128403df
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
337 additions
and
179 deletions
lib/bloc/web_cubit.dart
lib/bloc/wechat_auth_cubit.dart
lib/config/constant.dart
lib/config/locator.dart
lib/config/server.dart
lib/data/repositories/message/choose_file_handler.dart
lib/data/repositories/message/crop_image_handler.dart
lib/data/repositories/message/open_document_handler.dart
lib/data/repositories/message/upload_file.dart
lib/services/local_server_service.dart
pubspec.lock
pubspec.yaml
lib/bloc/web_cubit.dart
View file @
9c25964
...
...
@@ -2,6 +2,7 @@ import 'dart:async';
import
'dart:convert'
;
import
'dart:io'
;
import
'package:appframe/config/constant.dart'
;
import
'package:appframe/config/locator.dart'
;
import
'package:appframe/config/routes.dart'
;
import
'package:appframe/data/models/message/h5_message.dart'
;
...
...
@@ -510,6 +511,7 @@ class WebCubit extends Cubit<WebState> {
pickerConfig:
CameraPickerConfig
(
enableRecording:
true
,
onlyEnableRecording:
true
,
// enableTapRecording: true,
maximumRecordingDuration:
Duration
(
seconds:
maxDuration
),
),
);
...
...
@@ -704,7 +706,7 @@ class WebCubit extends Cubit<WebState> {
throw
Exception
(
"录音器未初始化"
);
}
if
(
state
.
recordState
!=
1
)
{
if
(
state
.
recordState
!=
1
&&
state
.
recordState
!=
2
)
{
throw
Exception
(
"录音器状态错误"
);
}
...
...
@@ -730,7 +732,10 @@ class WebCubit extends Cubit<WebState> {
// var duration = await AudioUtil.getAudioDuration(mp3Path);
var
duration
=
await
AudioUtil
.
getAudioDuration
(
url
);
return
{
'tempFilePath'
:
mp3Path
,
'duration'
:
duration
.
inSeconds
};
return
{
'tempFilePath'
:
'http://127.0.0.1:
${Constant.localServerPort}
/temp
$mp3Path
'
,
'duration'
:
duration
.
inSeconds
,
};
}
/// 清空录音
...
...
@@ -827,7 +832,7 @@ class WebCubit extends Cubit<WebState> {
throw
Exception
(
"播放器未初始化"
);
}
if
(
state
.
playState
!=
1
)
{
if
(
state
.
playState
!=
1
&&
state
.
playState
!=
2
)
{
throw
Exception
(
"播放器状态错误"
);
}
...
...
lib/bloc/wechat_auth_cubit.dart
View file @
9c25964
import
'package:appframe/config/constant.dart'
;
import
'package:appframe/config/locator.dart'
;
import
'package:appframe/config/routes.dart'
;
import
'package:appframe/data/repositories/wechat_auth_repository.dart'
;
...
...
@@ -62,10 +63,7 @@ class WechatAuthCubit extends Cubit<WechatAuthState> {
_fluwx
=
getIt
.
get
<
Fluwx
>();
_fluwx
.
addSubscriber
(
_responseListener
);
// _textEditingController = TextEditingController()..text = '127.0.0.1';
// _textEditingController = TextEditingController()..text = 'appdev-th.banxiaoer.net';
_textEditingController
=
TextEditingController
()..
text
=
'appdev-xj.banxiaoer.net'
;
// _textEditingController = TextEditingController()..text = '192.168.1.136';
_textEditingController
=
TextEditingController
()..
text
=
Constant
.
h5Server
;
_wechatAuthRepository
=
getIt
<
WechatAuthRepository
>();
}
...
...
@@ -88,10 +86,8 @@ class WechatAuthCubit extends Cubit<WechatAuthState> {
sharedPreferences
.
setString
(
'auth_userCode'
,
userCode
);
sharedPreferences
.
setString
(
'auth_classCode'
,
classCode
);
sharedPreferences
.
setInt
(
'auth_userType'
,
userType
);
sharedPreferences
.
setString
(
'auth_stuId'
,
stuId
??
''
);
// sharedPreferences.setString('auth_ip', 'appdev-th.banxiaoer.net');
sharedPreferences
.
setString
(
'auth_ip'
,
'appdev-xj.banxiaoer.net'
);
// sharedPreferences.setString('auth_ip', '192.168.1.136');
sharedPreferences
.
setString
(
'auth_stuId'
,
stuId
??
''
);
sharedPreferences
.
setString
(
'auth_ip'
,
Constant
.
h5Server
);
router
.
go
(
'/web'
,
...
...
lib/config/constant.dart
0 → 100644
View file @
9c25964
class
Constant
{
static
const
String
localServerHost
=
'127.0.0.1'
;
static
const
int
localServerPort
=
35982
;
static
const
String
localServerUrl
=
'http://
$localServerHost
:
$localServerPort
'
;
// static const String h5Server = 'appdev-xj.banxiaoer.net';
static
const
String
h5Server
=
'appdev-th.banxiaoer.net'
;
}
lib/config/locator.dart
View file @
9c25964
...
...
@@ -6,6 +6,7 @@ import 'package:appframe/data/repositories/message/choose_image_handler.dart';
import
'package:appframe/data/repositories/message/choose_video_handler.dart'
;
import
'package:appframe/data/repositories/message/clipboard_data_handler.dart'
;
import
'package:appframe/data/repositories/message/compress_handler.dart'
;
import
'package:appframe/data/repositories/message/crop_image_handler.dart'
;
import
'package:appframe/data/repositories/message/device_info_handler.dart'
;
import
'package:appframe/data/repositories/message/download_file_handler.dart'
;
import
'package:appframe/data/repositories/message/go_login_handler.dart'
;
...
...
@@ -57,13 +58,13 @@ Future<void> setupLocator() async {
/// 设备信息
getIt
.
registerLazySingleton
<
MessageHandler
>(()
=>
DeviceInfoHandler
(),
instanceName:
'getDeviceInfo'
);
// 位置信息
//
/
位置信息
getIt
.
registerLazySingleton
<
MessageHandler
>(()
=>
LocationHandler
(),
instanceName:
'getLocation'
);
// 网络信息
//
/
网络信息
getIt
.
registerLazySingleton
<
MessageHandler
>(()
=>
NetworkTypeHandler
(),
instanceName:
'getNetworkType'
);
// wifi信息
//
/
wifi信息
getIt
.
registerLazySingleton
<
MessageHandler
>(()
=>
WifiInfoHandler
(),
instanceName:
'getWifiInfo'
);
/// 设备方向
...
...
@@ -140,6 +141,9 @@ Future<void> setupLocator() async {
getIt
.
registerLazySingleton
<
MessageHandler
>(()
=>
AudioStopHandler
(),
instanceName:
'audioStop'
);
getIt
.
registerLazySingleton
<
MessageHandler
>(()
=>
AudioClearHandler
(),
instanceName:
'audioClear'
);
/// 裁剪图片
getIt
.
registerLazySingleton
<
MessageHandler
>(()
=>
CropImageHandler
(),
instanceName:
'cropImage'
);
/// 震动
getIt
.
registerLazySingleton
<
MessageHandler
>(()
=>
VibrateShortHandler
(),
instanceName:
'vibrateShort'
);
...
...
lib/config/server.dart
deleted
100644 → 0
View file @
128403d
final
int
serverPort
=
35982
;
lib/data/repositories/message/choose_file_handler.dart
View file @
9c25964
import
'dart:io'
;
import
'package:appframe/config/constant.dart'
;
import
'package:appframe/services/dispatcher.dart'
;
import
'package:appframe/utils/file_type_util.dart'
;
import
'package:appframe/utils/thumbnail_util.dart'
;
import
'package:file_picker/file_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'
;
import
'package:video_player/video_player.dart'
;
class
ChooseFileHandler
extends
MessageHandler
{
@override
...
...
@@ -16,7 +17,11 @@ class ChooseFileHandler extends MessageHandler {
throw
Exception
(
'参数错误'
);
}
final
count
=
params
[
'count'
]
as
int
;
var
count
=
1
;
if
(
params
.
containsKey
(
'count'
))
{
count
=
params
[
'count'
]
as
int
;
}
final
fileTypes
=
params
[
'fileTypes'
]
as
List
<
dynamic
>;
FilePickerResult
?
filePickerResult
=
await
FilePicker
.
platform
.
pickFiles
(
...
...
@@ -27,7 +32,7 @@ class ChooseFileHandler extends MessageHandler {
// 用户取消选择,返回空数组
if
(
filePickerResult
==
null
)
{
return
[]
;
throw
Exception
(
'cancel'
)
;
}
// 限制最多count个文件
...
...
@@ -40,21 +45,12 @@ class ChooseFileHandler extends MessageHandler {
result
.
add
(
await
_handleFile
(
file
));
}
return
result
;
return
{
'tempFiles'
:
result
}
;
}
Future
<
Map
<
String
,
dynamic
>>
_handleFile
(
PlatformFile
file
)
async
{
// 获取临时目录
final
tempDir
=
await
getTemporaryDirectory
();
// 临时文件路径
// final uniqueFileName = '${DateTime.now().millisecondsSinceEpoch}_${file.name}';
// final tempFilePath = path.join(tempDir.path, uniqueFileName);
// 所选中的文件已经被插件复制到临时目录,所以不需要再复制
final
originalFile
=
File
(
file
.
path
!);
// final copiedFile = await originalFile.copy(tempFilePath);
bool
isImage
=
await
FileTypeUtil
.
isImage
(
originalFile
);
bool
isVideo
=
false
;
...
...
@@ -62,25 +58,43 @@ class ChooseFileHandler extends MessageHandler {
isVideo
=
await
FileTypeUtil
.
isVideo
(
originalFile
);
}
// 通过image_size_getter获取图片尺寸
SizeResult
?
sizeResult
;
String
?
thumbTempFilePath
;
int
?
imgWidth
,
imgHeight
;
String
?
imgThumbFilePath
;
if
(
isImage
)
{
sizeResult
=
ImageSizeGetter
.
getSizeResult
(
FileInput
(
originalFile
));
thumbTempFilePath
=
await
ThumbnailUtil
.
genTempThumbnail
(
originalFile
,
tempDir
);
// 通过image_size_getter获取图片尺寸
var
sizeResult
=
ImageSizeGetter
.
getSizeResult
(
FileInput
(
originalFile
));
var
size
=
sizeResult
.
size
;
imgWidth
=
sizeResult
.
size
.
width
;
imgHeight
=
sizeResult
.
size
.
height
;
final
tempDir
=
await
getTemporaryDirectory
();
imgThumbFilePath
=
await
ThumbnailUtil
.
genTempThumbnail
(
originalFile
,
tempDir
);
}
double
?
videoWidth
,
videoHeight
;
String
?
videoThumbFilePath
;
if
(
isVideo
)
{
thumbTempFilePath
=
await
ThumbnailUtil
.
genVideoThumbnail
(
originalFile
.
path
,
tempDir
);
// 获取视频尺寸
VideoPlayerController
controller
=
VideoPlayerController
.
file
(
originalFile
);
await
controller
.
initialize
();
var
size
=
controller
.
value
.
size
;
videoWidth
=
size
.
width
;
videoHeight
=
size
.
height
;
controller
.
dispose
();
final
tempDir
=
await
getTemporaryDirectory
();
videoThumbFilePath
=
await
ThumbnailUtil
.
genVideoThumbnail
(
originalFile
.
path
,
tempDir
);
}
// 返回临时文件信息
return
{
'tempFilePath'
:
'
/temp
${originalF
ile.path}
'
,
'tempFilePath'
:
'
http://127.0.0.1:
${Constant.localServerPort}
/temp
${f
ile.path}
'
,
'size'
:
file
.
size
,
'width'
:
sizeResult
!=
null
?
sizeResult
.
size
.
width
:
''
,
'height'
:
sizeResult
!=
null
?
sizeResult
.
size
.
height
:
''
,
'thumbTempFilePath'
:
thumbTempFilePath
??
''
,
'fileType'
:
originalFile
.
path
.
split
(
'/'
).
last
.
split
(
'.'
).
last
,
'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
'
:
''
),
'fileType'
:
file
.
extension
,
};
}
}
lib/data/repositories/message/crop_image_handler.dart
View file @
9c25964
import
'dart:io'
;
import
'dart:typed_data'
;
import
'package:appframe/config/constant.dart'
;
import
'package:appframe/services/dispatcher.dart'
;
import
'package:dio/dio.dart'
;
import
'package:image/image.dart'
as
img
;
import
'package:path/path.dart'
as
path
;
import
'package:path_provider/path_provider.dart'
;
import
'package:uuid/uuid.dart'
;
class
CropImageHandler
extends
MessageHandler
{
@override
...
...
@@ -6,11 +15,11 @@ class CropImageHandler extends MessageHandler {
if
(
params
is
!
Map
<
String
,
dynamic
>)
{
throw
Exception
(
'参数错误'
);
}
var
url
=
params
[
'
sourceType
'
]
as
String
?;
var
url
=
params
[
'
url
'
]
as
String
?;
if
(
url
==
null
||
url
.
isEmpty
)
{
throw
Exception
(
'参数错误'
);
}
var
cropScale
=
params
[
'
s
cale'
]
as
String
?;
var
cropScale
=
params
[
'
cropS
cale'
]
as
String
?;
if
(
cropScale
==
null
||
cropScale
.
isEmpty
)
{
throw
Exception
(
'参数错误'
);
}
...
...
@@ -19,7 +28,78 @@ class CropImageHandler extends MessageHandler {
throw
Exception
(
'参数错误'
);
}
return
false
;
try
{
String
outputPath
=
await
_cropImageByRatio
(
url
,
cropScale
);
return
{
'tempFilePath'
:
'http://127.0.0.1:
${Constant.localServerPort}
/temp
$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
(
'/temp'
))
{
url
=
url
.
substring
(
5
);
}
String
extension
=
path
.
extension
(
url
);
Directory
cacheDirectory
=
await
getApplicationCacheDirectory
();
File
imageFile
;
// 对于远程图片,先下载到应用缓存目录,然后生成File
if
(
url
.
startsWith
(
'https://'
)
||
url
.
startsWith
(
'http://'
))
{
String
tempFilePath
=
'
${cacheDirectory.path}
/
${Uuid().v4()}$extension
'
;
Dio
dio
=
Dio
();
Response
resp
=
await
dio
.
download
(
url
,
tempFilePath
);
if
(
resp
.
statusCode
!=
200
)
{
throw
Exception
(
'下载图片失败'
);
}
dio
.
close
(
force:
true
);
imageFile
=
File
(
tempFilePath
);
}
// 对于本地路径,直接生成File
else
{
imageFile
=
File
(
url
);
}
// 1. 读取图片文件并解码
Uint8List
bytes
=
await
imageFile
.
readAsBytes
();
img
.
Image
originalImage
=
img
.
decodeImage
(
bytes
)!;
int
originalWidth
=
originalImage
.
width
;
int
originalHeight
=
originalImage
.
height
;
// 2. 计算目标裁剪区域
var
targetRatio
=
cropScale
.
split
(
":"
);
double
targetAspect
=
int
.
parse
(
targetRatio
[
0
])
/
int
.
parse
(
targetRatio
[
1
]);
// 例如 16/9
double
currentAspect
=
originalWidth
/
originalHeight
;
int
cropWidth
,
cropHeight
;
if
(
currentAspect
>
targetAspect
)
{
// 原图更宽,以高度为基准裁剪宽度
cropHeight
=
originalHeight
;
cropWidth
=
(
originalHeight
*
targetAspect
).
round
();
}
else
{
// 原图更高,以宽度为基准裁剪高度
cropWidth
=
originalWidth
;
cropHeight
=
(
originalWidth
/
targetAspect
).
round
();
}
// 3. 计算居中裁剪的起始点
int
startX
=
(
originalWidth
-
cropWidth
)
~/
2
;
int
startY
=
(
originalHeight
-
cropHeight
)
~/
2
;
// 4. 执行裁剪
img
.
Image
croppedImage
=
img
.
copyCrop
(
originalImage
,
x:
startX
,
y:
startY
,
width:
cropWidth
,
height:
cropHeight
);
// 5. 保存裁剪后的图片
String
outputPath
=
'
${cacheDirectory.path}
/
${Uuid().v4()}$extension
'
;
await
File
(
outputPath
).
writeAsBytes
(
img
.
encodeJpg
(
croppedImage
));
return
outputPath
;
}
}
lib/data/repositories/message/open_document_handler.dart
View file @
9c25964
import
'package:appframe/config/constant.dart'
;
import
'package:appframe/services/dispatcher.dart'
;
import
'package:open_file/open_file.dart'
;
import
'package:url_launcher/url_launcher.dart'
;
class
OpenDocumentHandler
extends
MessageHandler
{
...
...
@@ -7,12 +9,25 @@ class OpenDocumentHandler extends MessageHandler {
if
(
params
is
!
Map
<
String
,
dynamic
>)
{
throw
Exception
(
'参数错误'
);
}
final
url
=
params
[
'url'
]
as
String
;
var
url
=
params
[
'url'
]
as
String
;
if
(
url
.
isEmpty
)
{
throw
Exception
(
'参数错误'
);
}
return
await
_launchInBrowser
(
Uri
.
parse
(
url
));
if
(
url
.
startsWith
(
'http://127.0.0.1:
${Constant.localServerPort}
'
))
{
url
=
url
.
replaceFirst
(
'http://127.0.0.1:
${Constant.localServerPort}
'
,
''
);
}
if
(
url
.
startsWith
(
'/temp'
))
{
url
=
url
.
substring
(
5
);
}
if
(
url
.
startsWith
(
'http'
))
{
return
await
_launchInBrowser
(
Uri
.
parse
(
url
));
}
else
{
return
await
_open
(
url
);
}
}
Future
<
bool
>
_launchInBrowser
(
Uri
url
)
async
{
...
...
@@ -21,8 +36,11 @@ class OpenDocumentHandler extends MessageHandler {
}
else
{
throw
Exception
(
'Could not launch
$url
'
);
}
/*if (!await launchUrl(url, mode: LaunchMode.externalNonBrowserApplication)) {
throw Exception('Could not launch $url');
}*/
}
/// 暂时就简单打开一下,未做复杂调用
Future
<
bool
>
_open
(
String
filePath
)
async
{
var
r
=
await
OpenFile
.
open
(
"filePath"
);
return
r
.
type
==
ResultType
.
done
;
}
}
lib/data/repositories/message/upload_file.dart
View file @
9c25964
import
'dart:io'
;
import
'package:appframe/config/constant.dart'
;
import
'package:appframe/services/api_service.dart'
;
import
'package:appframe/services/dispatcher.dart'
;
import
'package:appframe/utils/file_type_util.dart'
;
...
...
@@ -19,7 +20,17 @@ class UploadFileHandler extends MessageHandler {
throw
Exception
(
'参数错误'
);
}
final
result
=
await
compute
(
_handleUpload
,
{
'filePath'
:
tempFilePath
});
final
String
?
busi
=
params
[
'busi'
]
as
String
?;
if
(
busi
==
null
||
busi
.
isEmpty
)
{
throw
Exception
(
'参数错误'
);
}
final
String
?
subBusi
=
params
[
'subBusi'
]
as
String
?;
if
(
subBusi
==
null
||
subBusi
.
isEmpty
)
{
throw
Exception
(
'参数错误'
);
}
final
result
=
await
compute
(
_handleUpload
,
{
'filePath'
:
tempFilePath
,
'busi'
:
busi
,
'subBusi'
:
subBusi
});
// final result = await _handleUpload({'filePath': tempFilePath});
return
result
;
...
...
@@ -32,19 +43,27 @@ class UploadFileHandler extends MessageHandler {
/// 在Isolate中执行
static
Future
<
Map
<
String
,
dynamic
>>
_handleUpload
(
Map
<
String
,
dynamic
>
fileParams
)
async
{
var
filePath
=
fileParams
[
'filePath'
]
as
String
;
String
filePath
=
fileParams
[
'filePath'
]
as
String
;
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}
'
,
''
);
}
if
(
filePath
.
startsWith
(
'/temp'
))
{
filePath
=
filePath
.
substring
(
5
);
}
String
logicPrefix
=
_getLoginPrefix
(
busi
,
subBusi
);
final
bxeApiService
=
ApiService
(
baseUrl:
_bxeBaseUrl
);
// 由于服务端签名时未设置Content-Type,这里必须设置为空,否则会报签名错误
// 由于封装有默认值,所以不能不设置
final
obsApiService
=
ApiService
(
defaultHeaders:
{
'Content-Type'
:
''
,
'Accept'
:
''
});
//并行上传分段
final
uploadResult
=
await
_uploadInParallel
(
bxeApiService
,
obsApiService
,
filePath
);
final
uploadResult
=
await
_uploadInParallel
(
bxeApiService
,
obsApiService
,
logicPrefix
,
filePath
);
String
objectKey
=
uploadResult
[
'objectKey'
]
as
String
;
String
bucket
=
uploadResult
[
'bucket'
]
as
String
;
String
uploadId
=
uploadResult
[
'uploadId'
]
as
String
;
...
...
@@ -57,15 +76,17 @@ class UploadFileHandler extends MessageHandler {
bxeApiService
.
close
();
obsApiService
.
close
();
return
{
'url'
:
response
.
data
[
'location'
]
};
return
{
'url'
:
_addPreUrl
(
response
.
data
[
"location"
])
};
}
/// 并行上传
static
Future
<
Map
<
String
,
dynamic
>>
_uploadInParallel
(
ApiService
bxeApiService
,
ApiService
obsApiService
,
String
filePath
,
{
int
maxConcurrency
=
5
,
})
async
{
static
Future
<
Map
<
String
,
dynamic
>>
_uploadInParallel
(
ApiService
bxeApiService
,
ApiService
obsApiService
,
String
logicPrefix
,
String
filePath
,
{
int
maxConcurrency
=
5
,
})
async
{
//判断文件
File
file
=
File
(
filePath
);
if
(!
file
.
existsSync
())
{
...
...
@@ -97,8 +118,7 @@ class UploadFileHandler extends MessageHandler {
//生成唯一文件名,
// String objectKey = 'd2/test/file.csv';
var
uuid
=
Uuid
();
// String objectKey = '${uuid.v5(Namespace.url.value, 'www.banxiaoer.com')}${path.extension(file.path)}';
String
objectKey
=
'
${uuid.v4()}${path.extension(file.path)}
'
;
String
objectKey
=
'
$logicPrefix
/
${uuid.v4()}${path.extension(file.path)}
'
;
String
uploadId
=
''
;
Map
<
int
,
String
>
tagsMap
=
{};
...
...
@@ -149,24 +169,27 @@ class UploadFileHandler extends MessageHandler {
}
/// 每次上传前,请求后端获取签名信息
static
Future
<
Map
<
String
,
dynamic
>>
_next
(
ApiService
bxeApiService
,
String
objectKey
,
String
bucket
,
String
uploadId
,
int
partNum
,)
async
{
static
Future
<
Map
<
String
,
dynamic
>>
_next
(
ApiService
bxeApiService
,
String
objectKey
,
String
bucket
,
String
uploadId
,
int
partNum
,
)
async
{
var
endpoint
=
'
$_signatureNextUrl
?objectKey=
$objectKey
&bucket=
$bucket
&uploadId=
$uploadId
&partNum=
$partNum
'
;
final
resp
=
await
bxeApiService
.
get
(
endpoint
);
return
resp
.
data
;
}
/// 上传段,按照最大重试次数进行上传重试
static
Future
<
void
>
_uploadChunkWithRetry
(
ApiService
obsApiService
,
String
signUrl
,
int
chunkIndex
,
Uint8List
chunk
,
Map
<
int
,
String
>
tagsMap
,
{
int
maxRetries
=
3
,
})
async
{
static
Future
<
void
>
_uploadChunkWithRetry
(
ApiService
obsApiService
,
String
signUrl
,
int
chunkIndex
,
Uint8List
chunk
,
Map
<
int
,
String
>
tagsMap
,
{
int
maxRetries
=
3
,
})
async
{
for
(
int
attempt
=
0
;
attempt
<=
maxRetries
;
attempt
++)
{
try
{
final
resp
=
await
_uploadChunk
(
obsApiService
,
signUrl
,
chunk
);
...
...
@@ -201,11 +224,13 @@ class UploadFileHandler extends MessageHandler {
}
/// 请求合并文件
static
Future
<
Response
>
_merge
(
ApiService
bxeApiService
,
String
objectKey
,
String
bucket
,
String
uploadId
,
Map
<
int
,
String
>
tagsMap
,)
async
{
static
Future
<
Response
>
_merge
(
ApiService
bxeApiService
,
String
objectKey
,
String
bucket
,
String
uploadId
,
Map
<
int
,
String
>
tagsMap
,
)
async
{
final
parts
=
[];
for
(
int
i
=
1
;
i
<=
tagsMap
.
length
;
i
++)
{
parts
.
add
({
'partNumber'
:
i
,
'etag'
:
tagsMap
[
i
]});
...
...
@@ -224,4 +249,27 @@ class UploadFileHandler extends MessageHandler {
return
response
;
}
static
String
_getLoginPrefix
(
String
busi
,
String
subBusi
)
{
var
now
=
DateTime
.
now
();
var
year
=
now
.
year
;
var
month
=
now
.
month
;
var
day
=
now
.
day
;
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位
if
(
location
.
startsWith
(
'/bxe-files'
))
{
return
'https://files-obs.banxiaoer.com
${location.substring(11)}
'
;
}
else
if
(
location
.
startsWith
(
'/bxe-pics'
))
{
return
'https://pics-obs.banxiaoer.com
${location.substring(10)}
'
;
}
else
if
(
location
.
startsWith
(
'/bxe-videos'
))
{
return
'https://videos-obs.banxiaoer.com
${location.substring(12)}
'
;
}
else
{
return
location
;
}
}
}
lib/services/local_server_service.dart
View file @
9c25964
import
'dart:io'
;
import
'package:appframe/config/constant.dart'
;
import
'package:archive/archive.dart'
;
import
'package:dio/dio.dart'
;
import
'package:flutter/services.dart'
;
...
...
@@ -13,7 +14,7 @@ class LocalServerService {
// 测试情况下, 每次启动服务,先解压dist文件
_extractDist
();
HttpServer
server
=
await
HttpServer
.
bind
(
InternetAddress
.
loopbackIPv4
,
35982
);
HttpServer
server
=
await
HttpServer
.
bind
(
InternetAddress
.
loopbackIPv4
,
Constant
.
localServerPort
);
print
(
'本地服务器启动在端口:
${server.port}
'
);
server
.
listen
((
HttpRequest
request
)
async
{
...
...
@@ -189,13 +190,19 @@ class LocalServerService {
var
distUrl
=
"https://github.com/xinxin-wu/flutter_web_dist/releases/download/v1.0.0/dist.zip"
;
// Dio进行下载
var
dio
=
Dio
();
dio
.
download
(
distUrl
,
'
$httpDirectory
/dist.zip'
,
onReceiveProgress:
(
received
,
total
)
{
if
(
total
!=
-
1
)
{
print
((
received
/
total
*
100
).
toStringAsFixed
(
0
)
+
"%"
);
}
}).
then
((
_
)
{
_extractDist
();
});
dio
.
download
(
distUrl
,
'
$httpDirectory
/dist.zip'
,
onReceiveProgress:
(
received
,
total
)
{
if
(
total
!=
-
1
)
{
print
((
received
/
total
*
100
).
toStringAsFixed
(
0
)
+
"%"
);
}
},
)
.
then
((
_
)
{
_extractDist
();
});
dio
.
close
();
}
}
pubspec.lock
View file @
9c25964
...
...
@@ -365,42 +365,10 @@ packages:
dependency: "direct main"
description:
name: file_picker
sha256:
e7e16c9d15c36330b94ca0e2ad8cb61f93cd5282d0158c09805aed13b5452f22
sha256:
f2d9f173c2c14635cc0e9b14c143c49ef30b4934e8d1d274d6206fcb0086a06f
url: "https://pub.flutter-io.cn"
source: hosted
version: "10.3.2"
file_selector_linux:
dependency: transitive
description:
name: file_selector_linux
sha256: "54cbbd957e1156d29548c7d9b9ec0c0ebb6de0a90452198683a7d23aed617a33"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.9.3+2"
file_selector_macos:
dependency: transitive
description:
name: file_selector_macos
sha256: "19124ff4a3d8864fdc62072b6a2ef6c222d55a3404fe14893a3c02744907b60c"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.9.4+4"
file_selector_platform_interface:
dependency: transitive
description:
name: file_selector_platform_interface
sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.6.2"
file_selector_windows:
dependency: transitive
description:
name: file_selector_windows
sha256: "320fcfb6f33caa90f0b58380489fc5ac05d99ee94b61aa96ec2bff0ba81d3c2b"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.9.3+4"
version: "10.3.3"
fixnum:
dependency: transitive
description:
...
...
@@ -709,70 +677,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.1.2"
image
_picker
:
image:
dependency: "direct main"
description:
name: image
_picker
sha256: "
736eb56a911cf24d1859315ad09ddec0b66104bc41a7f8c5b96b4e2620cf5041
"
name: image
sha256: "
4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928
"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.0"
image_picker_android:
dependency: transitive
description:
name: image_picker_android
sha256: dd7a61daaa5896cc34b7bc95f66c60225ae6bee0d167dde0e21a9d9016cac0dc
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.8.13+4"
image_picker_for_web:
dependency: transitive
description:
name: image_picker_for_web
sha256: "40c2a6a0da15556dc0f8e38a3246064a971a9f512386c3339b89f76db87269b6"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.0"
image_picker_ios:
dependency: transitive
description:
name: image_picker_ios
sha256: eb06fe30bab4c4497bad449b66448f50edcc695f1c59408e78aa3a8059eb8f0e
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.8.13"
image_picker_linux:
dependency: transitive
description:
name: image_picker_linux
sha256: "1f81c5f2046b9ab724f85523e4af65be1d47b038160a8c8deed909762c308ed4"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.2.2"
image_picker_macos:
dependency: transitive
description:
name: image_picker_macos
sha256: d58cd9d67793d52beefd6585b12050af0a7663c0c2a6ece0fb110a35d6955e04
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.2.2"
image_picker_platform_interface:
dependency: transitive
description:
name: image_picker_platform_interface
sha256: "9f143b0dba3e459553209e20cc425c9801af48e6dfa4f01a0fcf927be3f41665"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.11.0"
image_picker_windows:
dependency: transitive
description:
name: image_picker_windows
sha256: d248c86554a72b5495a31c56f060cf73a41c7ff541689327b1a7dbccc33adfae
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.2.2"
version: "4.5.4"
image_size_getter:
dependency: "direct main"
description:
...
...
@@ -941,6 +853,70 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.5.0"
open_file:
dependency: "direct main"
description:
name: open_file
sha256: d17e2bddf5b278cb2ae18393d0496aa4f162142ba97d1a9e0c30d476adf99c0e
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.5.10"
open_file_android:
dependency: transitive
description:
name: open_file_android
sha256: "58141fcaece2f453a9684509a7275f231ac0e3d6ceb9a5e6de310a7dff9084aa"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.6"
open_file_ios:
dependency: transitive
description:
name: open_file_ios
sha256: "02996f01e5f6863832068e97f8f3a5ef9b613516db6897f373b43b79849e4d07"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.3"
open_file_linux:
dependency: transitive
description:
name: open_file_linux
sha256: d189f799eecbb139c97f8bc7d303f9e720954fa4e0fa1b0b7294767e5f2d7550
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.0.5"
open_file_mac:
dependency: transitive
description:
name: open_file_mac
sha256: "1440b1e37ceb0642208cfeb2c659c6cda27b25187a90635c9d1acb7d0584d324"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.3"
open_file_platform_interface:
dependency: transitive
description:
name: open_file_platform_interface
sha256: "101b424ca359632699a7e1213e83d025722ab668b9fd1412338221bf9b0e5757"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.3"
open_file_web:
dependency: transitive
description:
name: open_file_web
sha256: e3dbc9584856283dcb30aef5720558b90f88036360bd078e494ab80a80130c4f
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.0.4"
open_file_windows:
dependency: transitive
description:
name: open_file_windows
sha256: d26c31ddf935a94a1a3aa43a23f4fff8a5ff4eea395fe7a8cb819cf55431c875
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.0.3"
package_config:
dependency: transitive
description:
...
...
@@ -1451,7 +1427,7 @@ packages:
source: hosted
version: "3.1.4"
video_player:
dependency:
transitive
dependency:
"direct main"
description:
name: video_player
sha256: "0d55b1f1a31e5ad4c4967bfaa8ade0240b07d20ee4af1dfef5f531056512961a"
...
...
pubspec.yaml
View file @
9c25964
...
...
@@ -47,12 +47,14 @@ dependencies:
get_it
:
^8.2.0
geolocator
:
^14.0.2
go_router
:
^16.2.1
image_picker
:
^1.2.0
# image_picker: ^1.2.0
image
:
^4.5.4
image_size_getter
:
^2.4.1
json_annotation
:
^4.9.0
mime
:
^2.0.0
mobile_scanner
:
^7.0.1
network_info_plus
:
^7.0.0
open_file
:
^3.5.10
path
:
^1.9.1
path_provider
:
^2.1.5
permission_handler
:
^12.0.1
...
...
@@ -61,6 +63,7 @@ dependencies:
uuid
:
^4.5.1
vibration
:
^3.1.3
video_compress
:
^3.1.4
video_player
:
^2.10.0
webview_flutter
:
^4.13.0
wechat_assets_picker
:
^9.8.0
wechat_camera_picker
:
^4.4.0
...
...
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