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

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