Hibok
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

454 rivejä
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. print(formData);
  130. response = await dio.post(uploadUrl + 'uploadchatfile', data: formData,
  131. onSendProgress: (int progress, int total) {
  132. streamController.sink.add(progress / total);
  133. streamLastPercentMap[msg.extraFile] = progress / total;
  134. }, cancelToken: cancelToken);
  135. streamController.close();
  136. streamMap.remove(msg.extraFile);
  137. cancelMap.remove(msg.extraFile);
  138. print('uploadFile response $response');
  139. print('###msgContent ${msg.msgContent.length}');
  140. if (response.data['code'] == 0) {
  141. var data = response.data['data'];
  142. print('uploadFile 上传成功');
  143. msg.state = MsgState.Uploaded;
  144. MsgHandler.sendChatMsg(msg);
  145. MessageMgr().emit('Update LastMsg', msg.sessionId);
  146. return data;
  147. } else {
  148. print('uploadFile 上传失败');
  149. msg.state = MsgState.UploadFailed;
  150. MessageMgr().emit('Update LastMsg', msg.sessionId);
  151. return null;
  152. }
  153. } on DioError catch (e) {
  154. if (CancelToken.isCancel(e)) {
  155. print('post请求取消! ' + e.message);
  156. }
  157. showToast('上传文件失败');
  158. msg.state = MsgState.UploadFailed;
  159. print('post请求发生错误:$e');
  160. }
  161. MessageMgr().emit('Update LastMsg', msg.sessionId);
  162. return null;
  163. }
  164. void cancelRequests(CancelToken cancelToken) {
  165. print('取消请求$cancelToken');
  166. cancelToken.cancel("cancelled");
  167. }
  168. String getFullUrl(String imgData, int sessionid, int channelType) {
  169. var data = {
  170. "fileid": imgData,
  171. 'targetid': sessionid,
  172. "userid": UserData().basicInfo.userId,
  173. 'channeltype': channelType
  174. };
  175. var sign = getSign(data);
  176. return uploadUrl +
  177. 'downloadchatfile?targetid=$sessionid&channeltype=$channelType&userid=${UserData().basicInfo.userId}&fileid=$imgData&sign=$sign';
  178. }
  179. copyFileUrl(MsgModel msg, BuildContext context) {
  180. var url = getFullUrl(msg.extraFile, msg.sessionId, msg.channelType);
  181. ClipboardData clipboardData = new ClipboardData(text: url);
  182. Clipboard.setData(clipboardData);
  183. showToast(I18n.of(context).successful_copy);
  184. }
  185. Future downloadFile(MsgModel msg, {CancelToken cancelToken}) async {
  186. Response response;
  187. var fullUrl = getFullUrl(msg.extraFile, msg.sessionId, msg.channelType);
  188. if (msg.extraFile.contains('http://')) {
  189. fullUrl = msg.extraFile;
  190. }
  191. print('下载文件$fullUrl');
  192. var path = await FileCacheMgr().genFilePath(msg.extraFile);
  193. if (msg.msgType == ChatType.ShortVoiceChatType.value) {
  194. path = path + '.wav';
  195. } else if (msg.msgType == ChatType.FileChatType.value) {
  196. var fileMsg = FileChat.fromBuffer(msg.msgContent);
  197. var fileName = fileMsg.name;
  198. path = await FileCacheMgr().genFilePath(fileName);
  199. }
  200. print('文件保存路径$path');
  201. try {
  202. var streamController = getStream(msg.extraFile);
  203. response = await dio.download(fullUrl, path,
  204. onReceiveProgress: (int progress, int total) {
  205. streamController.sink.add(progress / total);
  206. streamLastPercentMap[msg.extraFile] = progress / total;
  207. }, cancelToken: cancelToken);
  208. streamController.close();
  209. streamMap.remove(msg.extraFile);
  210. if (response.statusCode == 200) {
  211. msg.state = MsgState.DownloadSuccess;
  212. msg.localFile = path;
  213. SqlUtil().updateLocalFile(msg.extraFile, path,
  214. isGroup: msg.channelType == ChatChannelType.Group.value);
  215. return path;
  216. } else {
  217. msg.state = MsgState.DownloadFailed;
  218. showToast(I18n.of(Constants.getCurrentContext()).server_error_tips);
  219. return null;
  220. }
  221. } catch (e) {
  222. msg.state = MsgState.DownloadFailed;
  223. showToast(I18n.of(Constants.getCurrentContext()).server_error_tips);
  224. print(response.toString());
  225. }
  226. }
  227. //下载认证视频
  228. Future downloadCertifiedvideo(String url, downloadSuccess) async {
  229. Response response;
  230. var path = await FileCacheMgr().genFilePath(url);
  231. try {
  232. response = await dio.download(url, path);
  233. if (response.statusCode == 200) {
  234. if (downloadSuccess != null) downloadSuccess();
  235. return path;
  236. } else {
  237. showToast(I18n.of(Constants.getCurrentContext()).server_error_tips);
  238. return null;
  239. }
  240. } catch (e) {
  241. showToast(I18n.of(Constants.getCurrentContext()).server_error_tips);
  242. }
  243. }
  244. //机器人翻译,type 1文字,2语音
  245. Future<String> commitTranslateSource(
  246. int type, int sourceLang, int toLang, dynamic content) async {
  247. Response response;
  248. var myId = UserData().basicInfo.userId;
  249. Map<String, dynamic> data = {
  250. 'userid': myId,
  251. 'cType': type,
  252. 'ulanguage': sourceLang,
  253. 'tlanguage': toLang
  254. };
  255. data['sign'] = getSign(data);
  256. data['content'] = content;
  257. if (type == 2) {
  258. data['content'] = await MultipartFile.fromFile(content);
  259. }
  260. FormData formData = new FormData.fromMap(data);
  261. try {
  262. print('开始请求翻译$data ${uploadUrl + 'translate/transqt'}');
  263. response = await dio.post(rootUrl + '/translate/transqt', data: formData);
  264. Map resData = response.data;
  265. print('翻译结果$resData');
  266. if (resData['code'] == 0) {
  267. return resData['data'];
  268. }
  269. } catch (e) {
  270. print(e);
  271. }
  272. return null;
  273. }
  274. //获取群列表
  275. Future<List<GroupInfoModel>> getGroupList() async {
  276. Response response;
  277. var myId = UserData().basicInfo.userId;
  278. Map<String, dynamic> data = {
  279. 'userid': myId,
  280. };
  281. data['sign'] = getSign(data);
  282. try {
  283. print('开始请求群列表$data}');
  284. response = await dio.post(rootUrl + '/chat/queryusergroups', data: data);
  285. Map resData = response.data;
  286. if (resData['code'] == 0) {
  287. List resList = resData['data'];
  288. List<GroupInfoModel> groupList = [];
  289. for (var i = 0; i < resList.length; i++) {
  290. var info = GroupInfoServerModel.fromJson(resList[i]);
  291. groupList.add(GroupInfoModel.fromServerGroupInfo(info));
  292. }
  293. return groupList;
  294. }
  295. } catch (e) {
  296. print(e);
  297. }
  298. return null;
  299. }
  300. //获取多个群信息
  301. Future<List<GroupInfoModel>> getMoreGroupInfo(List<int> groupIds) async {
  302. Response response;
  303. var myId = UserData().basicInfo.userId;
  304. Map<String, dynamic> data = {
  305. 'userid': myId,
  306. };
  307. data['sign'] = getSign(data);
  308. data['groupids'] = groupIds;
  309. try {
  310. print('开始请求 getMoreGroupInfo $data');
  311. response = await dio.post(rootUrl + '/chat/querygroups', data: data);
  312. Map resData = response.data;
  313. if (resData['code'] == 0) {
  314. List data = resData['data'];
  315. print('群信息$data');
  316. if (data != null && data.length > 0) {
  317. List<GroupInfoModel> res = [];
  318. for (var each in data) {
  319. var info = GroupInfoServerModel.fromJson(each);
  320. res.add(GroupInfoModel.fromServerGroupInfo(info));
  321. }
  322. return res;
  323. }
  324. }
  325. } catch (e) {
  326. print(e);
  327. }
  328. return null;
  329. }
  330. //获取群信息
  331. Future<GroupInfoModel> getGroupInfo(int groupId) async {
  332. Response response;
  333. var myId = UserData().basicInfo.userId;
  334. Map<String, dynamic> data = {
  335. 'userid': myId,
  336. };
  337. data['sign'] = getSign(data);
  338. data['groupids'] = [groupId];
  339. try {
  340. print('开始请求群信息$data');
  341. response = await dio.post(rootUrl + '/chat/querygroups', data: data);
  342. Map resData = response.data;
  343. if (resData['code'] == 0) {
  344. List data = resData['data'];
  345. print('群信息$data');
  346. if (data != null && data.length > 0) {
  347. var info = GroupInfoServerModel.fromJson(data.first);
  348. return GroupInfoModel.fromServerGroupInfo(info);
  349. }
  350. }
  351. } catch (e) {
  352. print(e);
  353. }
  354. return null;
  355. }
  356. //获取群校验信息
  357. Future<Map> getGroupCheckInfo() async {
  358. Response response;
  359. var myId = UserData().basicInfo.userId;
  360. Map<String, dynamic> data = {
  361. 'userid': myId,
  362. };
  363. data['sign'] = getSign(data);
  364. try {
  365. print('请求获取群校验信息$data');
  366. response = await dio.post(rootUrl + '/chat/checkgroup', data: data);
  367. Map resData = response.data;
  368. if (resData['code'] == 0) {
  369. print('群校验信息${resData['data']}');
  370. return resData['data'];
  371. }
  372. } catch (e) {
  373. print(e);
  374. }
  375. return null;
  376. }
  377. }