Hibok
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 
 
 

453 wiersze
13 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. //下载认证视频
  227. Future downloadCertifiedvideo(String url, downloadSuccess) async {
  228. Response response;
  229. var path = await FileCacheMgr().genFilePath(url);
  230. try {
  231. response = await dio.download(url, path);
  232. if (response.statusCode == 200) {
  233. if (downloadSuccess != null) downloadSuccess();
  234. return path;
  235. } else {
  236. showToast(I18n.of(Constants.getCurrentContext()).server_error_tips);
  237. return null;
  238. }
  239. } catch (e) {
  240. showToast(I18n.of(Constants.getCurrentContext()).server_error_tips);
  241. }
  242. }
  243. //机器人翻译,type 1文字,2语音
  244. Future<String> commitTranslateSource(
  245. int type, int sourceLang, int toLang, dynamic content) async {
  246. Response response;
  247. var myId = UserData().basicInfo.userId;
  248. Map<String, dynamic> data = {
  249. 'userid': myId,
  250. 'cType': type,
  251. 'ulanguage': sourceLang,
  252. 'tlanguage': toLang
  253. };
  254. data['sign'] = getSign(data);
  255. data['content'] = content;
  256. if (type == 2) {
  257. data['content'] = await MultipartFile.fromFile(content);
  258. }
  259. FormData formData = new FormData.fromMap(data);
  260. try {
  261. print('开始请求翻译$data ${uploadUrl + 'translate/transqt'}');
  262. response = await dio.post(rootUrl + '/translate/transqt', data: formData);
  263. Map resData = response.data;
  264. print('翻译结果$resData');
  265. if (resData['code'] == 0) {
  266. return resData['data'];
  267. }
  268. } catch (e) {
  269. print(e);
  270. }
  271. return null;
  272. }
  273. //获取群列表
  274. Future<List<GroupInfoModel>> getGroupList() async {
  275. Response response;
  276. var myId = UserData().basicInfo.userId;
  277. Map<String, dynamic> data = {
  278. 'userid': myId,
  279. };
  280. data['sign'] = getSign(data);
  281. try {
  282. print('开始请求群列表$data}');
  283. response = await dio.post(rootUrl + '/chat/queryusergroups', data: data);
  284. Map resData = response.data;
  285. if (resData['code'] == 0) {
  286. List resList = resData['data'];
  287. List<GroupInfoModel> groupList = [];
  288. for (var i = 0; i < resList.length; i++) {
  289. var info = GroupInfoServerModel.fromJson(resList[i]);
  290. groupList.add(GroupInfoModel.fromServerGroupInfo(info));
  291. }
  292. return groupList;
  293. }
  294. } catch (e) {
  295. print(e);
  296. }
  297. return null;
  298. }
  299. //获取多个群信息
  300. Future<List<GroupInfoModel>> getMoreGroupInfo(List<int> groupIds) async {
  301. Response response;
  302. var myId = UserData().basicInfo.userId;
  303. Map<String, dynamic> data = {
  304. 'userid': myId,
  305. };
  306. data['sign'] = getSign(data);
  307. data['groupids'] = groupIds;
  308. try {
  309. print('开始请求 getMoreGroupInfo $data');
  310. response = await dio.post(rootUrl + '/chat/querygroups', data: data);
  311. Map resData = response.data;
  312. if (resData['code'] == 0) {
  313. List data = resData['data'];
  314. print('群信息$data');
  315. if (data != null && data.length > 0) {
  316. List<GroupInfoModel> res = [];
  317. for (var each in data) {
  318. var info = GroupInfoServerModel.fromJson(each);
  319. res.add(GroupInfoModel.fromServerGroupInfo(info));
  320. }
  321. return res;
  322. }
  323. }
  324. } catch (e) {
  325. print(e);
  326. }
  327. return null;
  328. }
  329. //获取群信息
  330. Future<GroupInfoModel> getGroupInfo(int groupId) async {
  331. Response response;
  332. var myId = UserData().basicInfo.userId;
  333. Map<String, dynamic> data = {
  334. 'userid': myId,
  335. };
  336. data['sign'] = getSign(data);
  337. data['groupids'] = [groupId];
  338. try {
  339. print('开始请求群信息$data');
  340. response = await dio.post(rootUrl + '/chat/querygroups', data: data);
  341. Map resData = response.data;
  342. if (resData['code'] == 0) {
  343. List data = resData['data'];
  344. print('群信息$data');
  345. if (data != null && data.length > 0) {
  346. var info = GroupInfoServerModel.fromJson(data.first);
  347. return GroupInfoModel.fromServerGroupInfo(info);
  348. }
  349. }
  350. } catch (e) {
  351. print(e);
  352. }
  353. return null;
  354. }
  355. //获取群校验信息
  356. Future<Map> getGroupCheckInfo() async {
  357. Response response;
  358. var myId = UserData().basicInfo.userId;
  359. Map<String, dynamic> data = {
  360. 'userid': myId,
  361. };
  362. data['sign'] = getSign(data);
  363. try {
  364. print('请求获取群校验信息$data');
  365. response = await dio.post(rootUrl + '/chat/checkgroup', data: data);
  366. Map resData = response.data;
  367. if (resData['code'] == 0) {
  368. print('群校验信息${resData['data']}');
  369. return resData['data'];
  370. }
  371. } catch (e) {
  372. print(e);
  373. }
  374. return null;
  375. }
  376. }