终端一体化运控平台
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

439 行
16 KiB

  1. using BPA.Helper;
  2. using BPASmartDatavDeviceClient.IoT;
  3. using DataVAPI.Tool.IOT;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.IO;
  7. using System.Linq;
  8. using System.Text;
  9. using System.Threading;
  10. using System.Threading.Tasks;
  11. using static AlibabaCloud.SDK.Iot20180120.Models.QueryDeviceFileResponseBody;
  12. namespace BPASmartClient.IoT.Model
  13. {
  14. /// <summary>
  15. /// 阿里云文件上传
  16. /// </summary>
  17. public class FileUpload
  18. {
  19. public static string FileName = $"HBL.LogDir{DateTime.Now.ToString("yyyy_M_d")}";
  20. public static string path = $"{System.AppDomain.CurrentDomain.BaseDirectory}LogDir\\{FileName}.log";
  21. /// <summary>
  22. /// 一次性上传的包
  23. /// </summary>
  24. public static byte[] FileBlock;
  25. /// <summary>
  26. /// 请求反馈信息
  27. /// </summary>
  28. public static FileUploadModelResult modelResult = null;
  29. /// <summary>
  30. /// 文件请求上传
  31. /// </summary>
  32. public static void FileRequest(DataVReport dataV)
  33. {
  34. TaskManage.GetInstance.Start(new Action(() =>
  35. {
  36. try
  37. {
  38. List<QueryDeviceFileResponseBodyData> queryDeviceFiles = Sample.FileQueryALL(DataVClient.GetInstance().DeviceName);
  39. QueryDeviceFileResponseBodyData _data = queryDeviceFiles?.Find(par => par.Name == FileName);
  40. if (_data != null)//删除文件
  41. {
  42. MessageLog.GetInstance.Show("阿里云设备下文件已存在,准备删除续传...");
  43. Sample.DeleteFile(DataVClient.GetInstance().DeviceName, _data.FileId);
  44. MessageLog.GetInstance.Show("删除成功.准备上传..");
  45. }
  46. byte[] FileBlockALL = FileContent(path);
  47. //分包上传
  48. int length = FileBlockALL.Count();
  49. int bfb = (FileBlockALL.Count() / 130000) + 1;
  50. MessageLog.GetInstance.Show($"[阿里云上传]:文件整理中总长度[{length}]字节,分包数[{bfb}]包.");
  51. //分包上传
  52. for (int i = 0; i < bfb; i++)
  53. {
  54. modelResult = null;
  55. byte[] data;
  56. if (i == bfb - 1) data = new byte[FileBlockALL.Count() - i * 130000];//最后一包
  57. else data = new byte[130000];//前面的包
  58. for (int k = 0; k < data.Length; k++)
  59. {
  60. data[k] = FileBlockALL[k + i * 130000];
  61. }
  62. FileBlock = data;
  63. FileUploadModel fileUpload = new FileUploadModel();
  64. fileUpload.@params.fileName = FileName;
  65. fileUpload.@params.fileSize = -1;
  66. fileUpload.@params.conflictStrategy = length > 130000 ? "append" : "overwrite";
  67. //fileUpload.@params.conflictStrategy = "append";// "overwrite";//覆盖模式
  68. //fileUpload.@params.ficMode = "crc64";
  69. //fileUpload.@params.ficValue = CRC.ToCRC16(FileBlock);
  70. fileUpload.@params.initUid = $"ab{RandomHelper.GenerateRandomCode()}";
  71. //上传到阿里云物联网平台的OSS存储空间中
  72. fileUpload.@params.extraParams.ossOwnerType = "iot-platform";
  73. //表示上传到设备所属用户自己的OSS存储空间中
  74. //{ossbucket}/aliyun-iot-device-file/${instanceId}/${productKey}/${serviceId}/${deviceName}/${fileName}
  75. //fileUpload.@params.extraParams.ossOwnerType = "device-user";
  76. //fileUpload.@params.extraParams.serviceId = "black";
  77. //fileUpload.@params.extraParams.fileTag = new Dictionary<string, string> { {"Time", DateTime.Now.ToString("yyyy_M_d") },{"Name", "HBL.LogDir" } };
  78. dataV.IOT_Publish(dataV.FileUpLoadTopic, Tools.JsonConvertTools(fileUpload));
  79. //MessageLog.GetInstance.Show($"[阿里云上传]:第[{i+1}]包数据,请求上传.");
  80. int count = 0;
  81. while (modelResult == null)//等待上传完成
  82. {
  83. if (count > 5)
  84. {
  85. MessageLog.GetInstance.Show($"[阿里云上传]:超时上传、未知原因、退出线程.");
  86. return;
  87. }
  88. Thread.Sleep(3000); count++;
  89. }
  90. ;
  91. if (modelResult.code == 200)
  92. {
  93. if (modelResult.data.complete)
  94. {
  95. MessageLog.GetInstance.Show($"[阿里云上传]:文件上传完成,总长度[{modelResult.data.bSize}]字节.");
  96. }
  97. }
  98. else
  99. MessageLog.GetInstance.Show($"[阿里云上传]:第[{i + 1}]包,上传失败.原因:{modelResult.message}");
  100. ;
  101. }
  102. }
  103. catch (Exception ex)
  104. {
  105. MessageLog.GetInstance.ShowEx(ex.ToString());
  106. }
  107. }), "文件请求上传", isRestart: false);
  108. }
  109. /// <summary>
  110. /// 文件上传
  111. /// </summary>
  112. public static void FileSend(DataVReport dataV, string uploadId, long offset)
  113. {
  114. FileSendModel fileSend = new FileSendModel();
  115. fileSend.@params.uploadId = uploadId;
  116. fileSend.@params.offset = offset;
  117. fileSend.@params.bSize = FileBlock.Count();
  118. fileSend.@params.isComplete = FileBlock.Count() == 130000 ? false : true;
  119. //结构如下图
  120. //Header.length(高 低) + Header(字节数组UTF-8) + 文件分片的字节数组 + "分片校验值CrC16 低 高"
  121. byte[] Header = Encoding.UTF8.GetBytes(Tools.JsonConvertTools(fileSend));
  122. byte HeaderLen_L = (byte)(Header.Length);
  123. byte HeaderLen_H = (byte)(Header.Length >> 8);
  124. byte[] CRC16 = CRC16Standard.getCRCBytes(FileBlock);//CRC.CRC16(FileBlock);
  125. List<byte> message = new List<byte>() { HeaderLen_H, HeaderLen_L };
  126. message.AddRange(Header);
  127. message.AddRange(FileBlock);
  128. message.AddRange(CRC16);
  129. dataV.IOT_Publish(dataV.FileUpLoadSendTopic, message.ToArray());
  130. }
  131. /// <summary>
  132. /// 取消文件上传
  133. /// </summary>
  134. public static void FileCancelSend(DataVReport dataV, string uploadId)
  135. {
  136. CancelSend cancel = new CancelSend(uploadId);
  137. dataV.IOT_Publish(dataV.CancelFileUpLoadSendTopic, Tools.JsonConvertTools(cancel));
  138. }
  139. /// <summary>
  140. /// 读取文件
  141. /// </summary>
  142. /// <param name="fileName"></param>
  143. /// <returns></returns>
  144. private static byte[] FileContent(string fileName)
  145. {
  146. using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
  147. {
  148. try
  149. {
  150. byte[] buffur = new byte[fs.Length];
  151. fs.Read(buffur, 0, (int)fs.Length);
  152. return buffur;
  153. }
  154. catch (Exception ex)
  155. {
  156. return new byte[0];
  157. }
  158. }
  159. }
  160. /// <summary>
  161. /// 读取文件字节
  162. /// </summary>
  163. /// <param name="path"></param>
  164. /// <returns></returns>
  165. public static byte[] ReadFile(string path)
  166. {
  167. //创建d:\file.txt的FileStream对象
  168. FileStream fstream = new FileStream($@"{path}", FileMode.OpenOrCreate);
  169. byte[] bData = new byte[fstream.Length];
  170. //设置流当前位置为文件开始位置
  171. fstream.Seek(0, SeekOrigin.Begin);
  172. //将文件的内容存到字节数组中(缓存)
  173. fstream.Read(bData, 0, bData.Length);
  174. string result = Encoding.UTF8.GetString(bData);
  175. Console.WriteLine(result);
  176. if (fstream != null)
  177. {
  178. //清除此流的缓冲区,使得所有缓冲的数据都写入到文件中
  179. fstream.Flush();
  180. fstream.Close();
  181. }
  182. return bData;
  183. }
  184. }
  185. #region 请求上传文件
  186. /// <summary>
  187. /// 请求上传文件
  188. /// </summary>
  189. public class FileUploadModel
  190. {
  191. /// <summary>
  192. /// 消息ID号
  193. /// 0~4294967295
  194. /// </summary>
  195. public string id { get; set; }
  196. /// <summary>
  197. /// 请求业务参数
  198. /// </summary>
  199. public BusinessParameters @params { get; set; }
  200. public FileUploadModel()
  201. {
  202. id = RandomHelper.GenerateRandomCode();
  203. @params = new BusinessParameters();
  204. }
  205. }
  206. /// <summary>
  207. /// 业务参数
  208. /// </summary>
  209. public class BusinessParameters
  210. {
  211. /// <summary>
  212. /// 设备上传文件的名称
  213. /// </summary>
  214. public string fileName { get; set; }
  215. /// <summary>
  216. /// 上传文件大小,单位字节。单个文件大小不超过16 MB。
  217. /// 取值为-1时,表示文件大小未知。文件上传完成时,需在上传文件分片的消息中,指定参数isComplete
  218. /// </summary>
  219. public long fileSize { get; set; }
  220. /// <summary>
  221. /// 物联网平台对设备上传同名文件的处理策略,默认为overwrite
  222. /// overwrite:覆盖模式 append:文件追加模式 reject:拒绝模式
  223. /// </summary>
  224. public string conflictStrategy { get; set; } = "overwrite";
  225. /// <summary>
  226. /// 文件的完整性校验模式,目前可取值crc64
  227. /// 若不传入,在文件上传完成后不校验文件完整性
  228. /// </summary>
  229. public string ficMode { get; set; }
  230. /// <summary>
  231. /// 文件的完整性校验值,是16位的Hex格式编码的字符串
  232. /// </summary>
  233. public string ficValue { get; set; }
  234. /// <summary>
  235. /// 自定义的设备请求上传文件的任务唯一ID,同一上传任务请求必须对应相同的唯一ID
  236. /// 不传入:物联网平台的云端识别当前请求为新的文件上传请求
  237. /// </summary>
  238. public string initUid { get; set; }
  239. /// <summary>
  240. /// 设备上传文件至OSS存储空间的配置参数
  241. /// </summary>
  242. public ConfigureParameters extraParams { get; set; } = new ConfigureParameters();
  243. }
  244. /// <summary>
  245. /// 配置参数
  246. /// </summary>
  247. public class ConfigureParameters
  248. {
  249. /// <summary>
  250. /// 设备上传文件的目标OSS所有者类型
  251. /// iot-platform:表示上传到阿里云物联网平台的OSS存储空间中
  252. /// device-user:表示上传到设备所属用户自己的OSS存储空间中
  253. /// </summary>
  254. public string ossOwnerType { get; set; } = "device-user";
  255. /// <summary>
  256. /// 文件上传相关的业务ID
  257. /// </summary>
  258. public string serviceId { get; set; }
  259. /// <summary>
  260. /// 标签
  261. /// </summary>
  262. public Dictionary<string, string> fileTag { get; set; }
  263. public ConfigureParameters()
  264. {
  265. }
  266. }
  267. #endregion
  268. #region 请求上传文件响应
  269. /// <summary>
  270. /// 请求响应
  271. /// </summary>
  272. public class FileUploadModelResult
  273. {
  274. /// <summary>
  275. /// 消息ID号
  276. /// 0~4294967295
  277. /// </summary>
  278. public string id { get; set; }
  279. /// <summary>
  280. /// 结果码。返回200表示成功
  281. /// </summary>
  282. public int code { get; set; }
  283. /// <summary>
  284. /// 请求失败时,返回的错误信息
  285. /// </summary>
  286. public string message { get; set; }
  287. /// <summary>
  288. /// 返回设备端的数据。
  289. /// </summary>
  290. public ResultData data { get; set; }
  291. }
  292. /// <summary>
  293. /// 请求响应数据
  294. /// </summary>
  295. public class ResultData
  296. {
  297. /// <summary>
  298. /// 设备上传文件的名称
  299. /// </summary>
  300. public string fileName { get; set; }
  301. /// <summary>
  302. /// 本次上传文件任务的标识ID。后续上传文件分片时,需要传递该文件标识ID。
  303. /// </summary>
  304. public string uploadId { get; set; }
  305. /// <summary>
  306. /// 仅当请求参数conflictStrategy为append,且物联网平台云端存在未完成上传的文件时,返回的已上传文件的大小,单位为字节。
  307. /// </summary>
  308. public long offset { get; set; }
  309. /// <summary>
  310. /// 当前上传文件分片的大小
  311. /// </summary>
  312. public long bSize { get; set; }
  313. /// <summary>
  314. /// 当上传了最后一个分片数据后,文件上传完成
  315. /// </summary>
  316. public bool complete { get; set; }
  317. /// <summary>
  318. /// 文件的完整性校验模式。若请求上传文件时传入了该参数,对应的值仅支持为crc64
  319. /// </summary>
  320. public string ficMode { get; set; }
  321. /// <summary>
  322. /// 文件上传完成,返回设备请求上传文件时的
  323. /// </summary>
  324. public string ficValueClient { get; set; }
  325. /// <summary>
  326. /// 文件上传完成,返回物联网平台云端计算的文件完整性校验值。该值与ficValueClient值相同,表示文件上传完整。
  327. /// </summary>
  328. public string ficValueServer { get; set; }
  329. }
  330. #endregion
  331. #region 上传文件
  332. public class FileSendModel
  333. {
  334. /// <summary>
  335. /// 消息ID号
  336. /// 0~4294967295
  337. /// </summary>
  338. public string id { get; set; }
  339. /// <summary>
  340. /// 上传业务参数
  341. /// </summary>
  342. public SendBusinessParameters @params { get; set; } = new SendBusinessParameters();
  343. public FileSendModel()
  344. {
  345. id = RandomHelper.GenerateRandomCode();
  346. }
  347. }
  348. /// <summary>
  349. /// 上传业务参数
  350. /// </summary>
  351. public class SendBusinessParameters
  352. {
  353. /// <summary>
  354. /// 设备请求上传文件时返回的文件上传任务标识ID。
  355. /// </summary>
  356. public string uploadId { get; set; }
  357. /// <summary>
  358. /// 已上传文件分片的总大小,单位为字节。
  359. /// </summary>
  360. public long offset { get; set; }
  361. /// <summary>
  362. /// 当前上传文件分片的大小,单位为字节
  363. /// </summary>
  364. public long bSize { get; set; }
  365. /// <summary>
  366. /// 仅当设备请求上传文件中fileSize为-1,即文件大小未知时,该参数有效,表示当前分片是否是文件的最后一个分片
  367. /// </summary>
  368. public bool isComplete { get; set; } = true;
  369. }
  370. #endregion
  371. #region 取消文件上传
  372. /// <summary>
  373. /// 取消上传
  374. /// </summary>
  375. public class CancelSend
  376. {
  377. /// <summary>
  378. /// 消息ID号
  379. /// 0~4294967295
  380. /// </summary>
  381. public string id { get; set; }
  382. /// <summary>
  383. /// 请求业务参数
  384. /// </summary>
  385. public object @params { get; set; }
  386. public CancelSend(string uploadId)
  387. {
  388. id = RandomHelper.GenerateRandomCode();
  389. @params = new { uploadId = uploadId };
  390. }
  391. }
  392. #endregion
  393. public class RandomHelper
  394. {
  395. /// <summary>
  396. ///生成制定位数的随机码(数字)
  397. /// </summary>
  398. /// <param name="length"></param>
  399. /// <returns></returns>
  400. public static string GenerateRandomCode(int length = 10)
  401. {
  402. var result = new StringBuilder();
  403. for (var i = 0; i < length; i++)
  404. {
  405. var r = new Random(Guid.NewGuid().GetHashCode());
  406. result.Append(r.Next(0, 10));
  407. }
  408. return result.ToString();
  409. }
  410. }
  411. }