Hibok
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

435 行
12 KiB

  1. import 'dart:convert';
  2. import 'package:chat/data/UserData.dart';
  3. import 'package:chat/generated/i18n.dart';
  4. import 'package:chat/models/ChatMsg.dart';
  5. import 'package:chat/models/group_info_model.dart';
  6. import 'package:chat/models/group_info_server_model.dart';
  7. import 'package:chat/proto/all.pbserver.dart';
  8. import 'package:chat/data/constants.dart';
  9. import 'package:chat/utils/file_cache_mgr.dart';
  10. import 'package:convert/convert.dart';
  11. import 'package:dio/dio.dart';
  12. import 'package:crypto/crypto.dart';
  13. import 'package:flutter/material.dart';
  14. import 'package:flutter/services.dart';
  15. import 'dart:async';
  16. import 'package:http_parser/http_parser.dart';
  17. import 'package:chat/utils/MessageMgr.dart';
  18. import 'package:oktoast/oktoast.dart';
  19. import 'package:chat/utils/msgHandler.dart';
  20. import 'package:chat/utils/sql_util.dart';
  21. var signKey = 'O@MlSxWaeAlc5CYu';
  22. class UploadUtil {
  23. // 工厂模式
  24. factory UploadUtil() => _getInstance();
  25. static UploadUtil get instance => _getInstance();
  26. static UploadUtil _instance;
  27. UploadUtil._internal() {
  28. // 初始化
  29. }
  30. static UploadUtil _getInstance() {
  31. if (_instance == null) {
  32. _instance = new UploadUtil._internal();
  33. }
  34. return _instance;
  35. }
  36. Dio dio;
  37. //进度流
  38. Map<String, StreamController<double>> streamMap = {};
  39. //取消map
  40. Map<String, CancelToken> cancelMap = {};
  41. //缓存进度,方便快速显示
  42. Map<String, double> streamLastPercentMap = {};
  43. //上传地址
  44. String uploadUrl;
  45. String rootUrl;
  46. void setUploadUrl(url) {
  47. rootUrl = 'http://' + url;
  48. uploadUrl = 'http://' + url + '/chatresfile/';
  49. BaseOptions options = BaseOptions(
  50. baseUrl: uploadUrl,
  51. headers: {},
  52. );
  53. print(uploadUrl);
  54. dio = new Dio(options);
  55. }
  56. // md5 加密
  57. String generateMd5(String data) {
  58. var content = new Utf8Encoder().convert(data);
  59. var digest = md5.convert(content);
  60. // 这里其实就是 digest.toString()
  61. return hex.encode(digest.bytes);
  62. }
  63. String getSign(Map parameter) {
  64. String k = '';
  65. var keyList = [];
  66. for (var key in parameter.keys) {
  67. keyList.add(key);
  68. }
  69. keyList.sort();
  70. for (var key in keyList) {
  71. k += '$key=${parameter[key]}&';
  72. }
  73. k += 'key=O@MlSxWaeAlc5CYu';
  74. return generateMd5(k);
  75. }
  76. String getFileType(String suffix) {
  77. if (suffix == 'png' || suffix == 'jpg' || suffix == 'jpeg') {
  78. return 'image';
  79. } else if (suffix == 'wav') {
  80. return 'audio';
  81. } else {
  82. return 'video';
  83. }
  84. }
  85. bool isImage(String suffix) {
  86. suffix = suffix.toLowerCase();
  87. return suffix == 'png' || suffix == 'jpg' || suffix == 'jpeg';
  88. }
  89. StreamController<double> getStream(String fileId) {
  90. if (streamMap[fileId] == null) {
  91. streamMap[fileId] = StreamController<double>.broadcast();
  92. }
  93. return streamMap[fileId];
  94. }
  95. getCancelToken(String fileId) {
  96. if (cancelMap[fileId] == null) {
  97. cancelMap[fileId] = CancelToken();
  98. }
  99. return cancelMap[fileId];
  100. }
  101. cancelSendMsg(MsgModel msgModel){
  102. var token = cancelMap[msgModel.extraFile];
  103. if (token != null) {
  104. token.cancel('用户取消');
  105. }
  106. }
  107. Future uploadFile(MsgModel msg, {CancelToken cancelToken}) async {
  108. Response response;
  109. var data = {
  110. "upuserid": msg.from,
  111. 'channeltype': msg.channelType,
  112. "targetid": msg.sessionId,
  113. 'fileid': msg.extraFile,
  114. };
  115. print('上传参数$data');
  116. var path = msg.localFile;
  117. var name = path.substring(path.lastIndexOf("/") + 1, path.length);
  118. String suffix = name.substring(name.lastIndexOf(".") + 1, name.length);
  119. var fileType = getFileType(suffix);
  120. print('文件类型 : $fileType $suffix');
  121. data['sign'] = getSign(data);
  122. data['uploadfile'] = await MultipartFile.fromFile(path,
  123. filename: name, contentType: MediaType(fileType, suffix));
  124. FormData formData = new FormData.fromMap(data);
  125. try {
  126. print('开始上传文件${msg.extraFile} url:${uploadUrl + 'uploadchatfile'}');
  127. var streamController = getStream(msg.extraFile);
  128. var cancelToken = getCancelToken(msg.extraFile);
  129. response = await dio.post(uploadUrl + 'uploadchatfile', data: formData,
  130. onSendProgress: (int progress, int total) {
  131. streamController.sink.add(progress / total);
  132. streamLastPercentMap[msg.extraFile] = progress / total;
  133. }, cancelToken: cancelToken);
  134. streamController.close();
  135. streamMap.remove(msg.extraFile);
  136. cancelMap.remove(msg.extraFile);
  137. print('uploadFile response $response');
  138. print('###msgContent ${msg.msgContent.length}');
  139. if (response.data['code'] == 0) {
  140. var data = response.data['data'];
  141. print('uploadFile 上传成功');
  142. msg.state = MsgState.Uploaded;
  143. MsgHandler.sendChatMsg(msg);
  144. MessageMgr().emit('Update LastMsg', msg.sessionId);
  145. return data;
  146. } else {
  147. print('uploadFile 上传失败');
  148. msg.state = MsgState.UploadFailed;
  149. MessageMgr().emit('Update LastMsg', msg.sessionId);
  150. return null;
  151. }
  152. } on DioError catch (e) {
  153. if (CancelToken.isCancel(e)) {
  154. print('post请求取消! ' + e.message);
  155. }
  156. showToast('上传文件失败');
  157. msg.state = MsgState.UploadFailed;
  158. print('post请求发生错误:$e');
  159. }
  160. MessageMgr().emit('Update LastMsg', msg.sessionId);
  161. return null;
  162. }
  163. void cancelRequests(CancelToken cancelToken) {
  164. print('取消请求$cancelToken');
  165. cancelToken.cancel("cancelled");
  166. }
  167. String getFullUrl(String imgData, int sessionid, int channelType) {
  168. var data = {
  169. "fileid": imgData,
  170. 'targetid': sessionid,
  171. "userid": UserData().basicInfo.userId,
  172. 'channeltype': channelType
  173. };
  174. var sign = getSign(data);
  175. return uploadUrl +
  176. 'downloadchatfile?targetid=$sessionid&channeltype=$channelType&userid=${UserData().basicInfo.userId}&fileid=$imgData&sign=$sign';
  177. }
  178. copyFileUrl(MsgModel msg, BuildContext context) {
  179. var url = getFullUrl(msg.extraFile, msg.sessionId, msg.channelType);
  180. ClipboardData clipboardData = new ClipboardData(text: url);
  181. Clipboard.setData(clipboardData);
  182. showToast(I18n.of(context).successful_copy);
  183. }
  184. Future downloadFile(MsgModel msg, {CancelToken cancelToken}) async {
  185. Response response;
  186. var fullUrl = getFullUrl(msg.extraFile, msg.sessionId, msg.channelType);
  187. if (msg.extraFile.contains('http://')) {
  188. fullUrl = msg.extraFile;
  189. }
  190. print('下载文件$fullUrl');
  191. var path = await FileCacheMgr().genFilePath(msg.extraFile);
  192. if (msg.msgType == ChatType.ShortVoiceChatType.value) {
  193. path = path + '.wav';
  194. } else if (msg.msgType == ChatType.FileChatType.value) {
  195. var fileMsg = FileChat.fromBuffer(msg.msgContent);
  196. var fileName = fileMsg.name;
  197. path = await FileCacheMgr().genFilePath(fileName);
  198. }
  199. print('文件保存路径$path');
  200. try {
  201. var streamController = getStream(msg.extraFile);
  202. response = await dio.download(fullUrl, path,
  203. onReceiveProgress: (int progress, int total) {
  204. streamController.sink.add(progress / total);
  205. streamLastPercentMap[msg.extraFile] = progress / total;
  206. }, cancelToken: cancelToken);
  207. streamController.close();
  208. streamMap.remove(msg.extraFile);
  209. if (response.statusCode == 200) {
  210. msg.state = MsgState.DownloadSuccess;
  211. msg.localFile = path;
  212. SqlUtil().updateLocalFile(msg.extraFile, path,
  213. isGroup: msg.channelType == ChatChannelType.Group.value);
  214. return path;
  215. } else {
  216. msg.state = MsgState.DownloadFailed;
  217. showToast(I18n.of(Constants.getCurrentContext()).server_error_tips);
  218. return null;
  219. }
  220. } catch (e) {
  221. msg.state = MsgState.DownloadFailed;
  222. showToast(I18n.of(Constants.getCurrentContext()).server_error_tips);
  223. print(response.toString());
  224. }
  225. }
  226. //机器人翻译,type 1文字,2语音
  227. Future<String> commitTranslateSource(
  228. int type, int sourceLang, int toLang, dynamic content) async {
  229. Response response;
  230. var myId = UserData().basicInfo.userId;
  231. Map<String, dynamic> data = {
  232. 'userid': myId,
  233. 'cType': type,
  234. 'ulanguage': sourceLang,
  235. 'tlanguage': toLang
  236. };
  237. data['sign'] = getSign(data);
  238. data['content'] = content;
  239. if (type == 2) {
  240. data['content'] = await MultipartFile.fromFile(content);
  241. }
  242. FormData formData = new FormData.fromMap(data);
  243. try {
  244. print('开始请求翻译$data ${uploadUrl + 'translate/transqt'}');
  245. response = await dio.post(rootUrl + '/translate/transqt', data: formData);
  246. Map resData = response.data;
  247. print('翻译结果$resData');
  248. if (resData['code'] == 0) {
  249. return resData['data'];
  250. }
  251. } catch (e) {
  252. print(e);
  253. }
  254. return null;
  255. }
  256. //获取群列表
  257. Future<List<GroupInfoModel>> getGroupList() async {
  258. Response response;
  259. var myId = UserData().basicInfo.userId;
  260. Map<String, dynamic> data = {
  261. 'userid': myId,
  262. };
  263. data['sign'] = getSign(data);
  264. try {
  265. print('开始请求群列表$data}');
  266. response = await dio.post(rootUrl + '/chat/queryusergroups', data: data);
  267. Map resData = response.data;
  268. if (resData['code'] == 0) {
  269. List resList = resData['data'];
  270. List<GroupInfoModel> groupList = [];
  271. for (var i = 0; i < resList.length; i++) {
  272. var info = GroupInfoServerModel.fromJson(resList[i]);
  273. groupList.add(GroupInfoModel.fromServerGroupInfo(info));
  274. }
  275. return groupList;
  276. }
  277. } catch (e) {
  278. print(e);
  279. }
  280. return null;
  281. }
  282. //获取多个群信息
  283. Future<List<GroupInfoModel>> getMoreGroupInfo(List<int> groupIds) async {
  284. Response response;
  285. var myId = UserData().basicInfo.userId;
  286. Map<String, dynamic> data = {
  287. 'userid': myId,
  288. };
  289. data['sign'] = getSign(data);
  290. data['groupids'] = groupIds;
  291. try {
  292. print('开始请求 getMoreGroupInfo $data');
  293. response = await dio.post(rootUrl + '/chat/querygroups', data: data);
  294. Map resData = response.data;
  295. if (resData['code'] == 0) {
  296. List data = resData['data'];
  297. print('群信息$data');
  298. if (data != null && data.length > 0) {
  299. List<GroupInfoModel> res = [];
  300. for (var each in data) {
  301. var info = GroupInfoServerModel.fromJson(each);
  302. res.add(GroupInfoModel.fromServerGroupInfo(info));
  303. }
  304. return res;
  305. }
  306. }
  307. } catch (e) {
  308. print(e);
  309. }
  310. return null;
  311. }
  312. //获取群信息
  313. Future<GroupInfoModel> getGroupInfo(int groupId) async {
  314. Response response;
  315. var myId = UserData().basicInfo.userId;
  316. Map<String, dynamic> data = {
  317. 'userid': myId,
  318. };
  319. data['sign'] = getSign(data);
  320. data['groupids'] = [groupId];
  321. try {
  322. print('开始请求群信息$data');
  323. response = await dio.post(rootUrl + '/chat/querygroups', data: data);
  324. Map resData = response.data;
  325. if (resData['code'] == 0) {
  326. List data = resData['data'];
  327. print('群信息$data');
  328. if (data != null && data.length > 0) {
  329. var info = GroupInfoServerModel.fromJson(data.first);
  330. return GroupInfoModel.fromServerGroupInfo(info);
  331. }
  332. }
  333. } catch (e) {
  334. print(e);
  335. }
  336. return null;
  337. }
  338. //获取群校验信息
  339. Future<Map> getGroupCheckInfo() async {
  340. Response response;
  341. var myId = UserData().basicInfo.userId;
  342. Map<String, dynamic> data = {
  343. 'userid': myId,
  344. };
  345. data['sign'] = getSign(data);
  346. try {
  347. print('请求获取群校验信息$data');
  348. response = await dio.post(rootUrl + '/chat/checkgroup', data: data);
  349. Map resData = response.data;
  350. if (resData['code'] == 0) {
  351. print('群校验信息${resData['data']}');
  352. return resData['data'];
  353. }
  354. } catch (e) {
  355. print(e);
  356. }
  357. return null;
  358. }
  359. }