|
- using BPA.Helper;
- using BPA.Message.IOT;
- using BPASmartDatavDeviceClient.IoT;
- using DataVAPI.Tool.IOT;
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
- using static AlibabaCloud.SDK.Iot20180120.Models.QueryDeviceFileResponseBody;
-
- namespace BPASmartClient.IoT.Model
- {
- /// <summary>
- /// 阿里云文件上传
- /// </summary>
- public class FileUpload
- {
- public static string FileName = $"HBL.LogDir{DateTime.Now.ToString("yyyy_M_d")}";
- public static string path = $"{System.AppDomain.CurrentDomain.BaseDirectory}LogDir\\{FileName}.log";
- /// <summary>
- /// 一次性上传的包
- /// </summary>
- public static byte[] FileBlock;
- /// <summary>
- /// 请求反馈信息
- /// </summary>
- public static FileUploadModelResult modelResult = null;
- /// <summary>
- /// 文件请求上传
- /// </summary>
- public static void FileRequest(DataVReport dataV)
- {
- TaskManage.GetInstance.Start(new Action(() =>
- {
- try
- {
- List<QueryDeviceFileResponseBodyData> queryDeviceFiles = Sample.FileQueryALL(DataVClient.GetInstance.DeviceName);
- QueryDeviceFileResponseBodyData _data = queryDeviceFiles?.Find(par => par.Name == FileName);
- if (_data != null)//删除文件
- {
- MessageLog.GetInstance.Show("阿里云设备下文件已存在,准备删除续传...");
- Sample.DeleteFile(DataVClient.GetInstance.DeviceName, _data.FileId);
- MessageLog.GetInstance.Show("删除成功.准备上传..");
- }
- byte[] FileBlockALL = FileContent(path);
- //分包上传
- int length = FileBlockALL.Count();
- int bfb = (FileBlockALL.Count() / 130000) + 1;
- MessageLog.GetInstance.Show($"[阿里云上传]:文件整理中总长度[{length}]字节,分包数[{bfb}]包.");
- //分包上传
- for (int i = 0; i < bfb; i++)
- {
- modelResult = null;
- byte[] data;
- if (i == bfb - 1) data = new byte[FileBlockALL.Count() - i * 130000];//最后一包
- else data = new byte[130000];//前面的包
-
- for (int k = 0; k < data.Length; k++)
- {
- data[k] = FileBlockALL[k + i * 130000];
- }
- FileBlock = data;
-
- FileUploadModel fileUpload = new FileUploadModel();
- fileUpload.@params.fileName = FileName;
- fileUpload.@params.fileSize = -1;
-
- fileUpload.@params.conflictStrategy = length > 130000 ? "append" : "overwrite";
- //fileUpload.@params.conflictStrategy = "append";// "overwrite";//覆盖模式
- //fileUpload.@params.ficMode = "crc64";
- //fileUpload.@params.ficValue = CRC.ToCRC16(FileBlock);
- fileUpload.@params.initUid = $"ab{RandomHelper.GenerateRandomCode()}";
-
- //上传到阿里云物联网平台的OSS存储空间中
- fileUpload.@params.extraParams.ossOwnerType = "iot-platform";
-
- //表示上传到设备所属用户自己的OSS存储空间中
- //{ossbucket}/aliyun-iot-device-file/${instanceId}/${productKey}/${serviceId}/${deviceName}/${fileName}
- //fileUpload.@params.extraParams.ossOwnerType = "device-user";
- //fileUpload.@params.extraParams.serviceId = "black";
- //fileUpload.@params.extraParams.fileTag = new Dictionary<string, string> { {"Time", DateTime.Now.ToString("yyyy_M_d") },{"Name", "HBL.LogDir" } };
-
- dataV.IOT_Publish(dataV.FileUpLoadTopic, Tools.JsonConvertTools(fileUpload));
- //MessageLog.GetInstance.Show($"[阿里云上传]:第[{i+1}]包数据,请求上传.");
-
- int count = 0;
- while (modelResult == null)//等待上传完成
- {
- if (count > 5)
- {
- MessageLog.GetInstance.Show($"[阿里云上传]:超时上传、未知原因、退出线程.");
-
- return;
- }
- Thread.Sleep(3000); count++;
- }
-
- ;
-
- if (modelResult.code == 200)
- {
- if (modelResult.data.complete)
- {
- MessageLog.GetInstance.Show($"[阿里云上传]:文件上传完成,总长度[{modelResult.data.bSize}]字节.");
- }
- }
- else
- MessageLog.GetInstance.Show($"[阿里云上传]:第[{i + 1}]包,上传失败.原因:{modelResult.message}");
- ;
- }
- }
- catch (Exception ex)
- {
- MessageLog.GetInstance.ShowEx(ex.ToString());
- }
- }), "文件请求上传", isRestart: false);
- }
-
- /// <summary>
- /// 文件上传
- /// </summary>
- public static void FileSend(DataVReport dataV, string uploadId, long offset)
- {
- FileSendModel fileSend = new FileSendModel();
- fileSend.@params.uploadId = uploadId;
- fileSend.@params.offset = offset;
- fileSend.@params.bSize = FileBlock.Count();
- fileSend.@params.isComplete = FileBlock.Count() == 130000 ? false : true;
- //结构如下图
- //Header.length(高 低) + Header(字节数组UTF-8) + 文件分片的字节数组 + "分片校验值CrC16 低 高"
- byte[] Header = Encoding.UTF8.GetBytes(Tools.JsonConvertTools(fileSend));
- byte HeaderLen_L = (byte)(Header.Length);
- byte HeaderLen_H = (byte)(Header.Length >> 8);
- byte[] CRC16 = CRC16Standard.getCRCBytes(FileBlock);//CRC.CRC16(FileBlock);
- List<byte> message = new List<byte>() { HeaderLen_H, HeaderLen_L };
- message.AddRange(Header);
- message.AddRange(FileBlock);
- message.AddRange(CRC16);
- dataV.IOT_Publish(dataV.FileUpLoadSendTopic, message.ToArray());
- }
-
- /// <summary>
- /// 取消文件上传
- /// </summary>
- public static void FileCancelSend(DataVReport dataV, string uploadId)
- {
- CancelSend cancel = new CancelSend(uploadId);
- dataV.IOT_Publish(dataV.CancelFileUpLoadSendTopic, Tools.JsonConvertTools(cancel));
- }
-
-
- /// <summary>
- /// 读取文件
- /// </summary>
- /// <param name="fileName"></param>
- /// <returns></returns>
- private static byte[] FileContent(string fileName)
- {
- using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
- {
- try
- {
- byte[] buffur = new byte[fs.Length];
- fs.Read(buffur, 0, (int)fs.Length);
- return buffur;
- }
- catch (Exception ex)
- {
- return new byte[0];
- }
- }
- }
- /// <summary>
- /// 读取文件字节
- /// </summary>
- /// <param name="path"></param>
- /// <returns></returns>
- public static byte[] ReadFile(string path)
- {
- //创建d:\file.txt的FileStream对象
- FileStream fstream = new FileStream($@"{path}", FileMode.OpenOrCreate);
- byte[] bData = new byte[fstream.Length];
- //设置流当前位置为文件开始位置
- fstream.Seek(0, SeekOrigin.Begin);
-
- //将文件的内容存到字节数组中(缓存)
- fstream.Read(bData, 0, bData.Length);
- string result = Encoding.UTF8.GetString(bData);
- Console.WriteLine(result);
- if (fstream != null)
- {
- //清除此流的缓冲区,使得所有缓冲的数据都写入到文件中
- fstream.Flush();
- fstream.Close();
- }
- return bData;
- }
- }
-
- #region 请求上传文件
- /// <summary>
- /// 请求上传文件
- /// </summary>
- public class FileUploadModel
- {
- /// <summary>
- /// 消息ID号
- /// 0~4294967295
- /// </summary>
- public string id { get; set; }
- /// <summary>
- /// 请求业务参数
- /// </summary>
- public BusinessParameters @params { get; set; }
-
- public FileUploadModel()
- {
- id = RandomHelper.GenerateRandomCode();
- @params = new BusinessParameters();
- }
- }
-
- /// <summary>
- /// 业务参数
- /// </summary>
- public class BusinessParameters
- {
- /// <summary>
- /// 设备上传文件的名称
- /// </summary>
- public string fileName { get; set; }
- /// <summary>
- /// 上传文件大小,单位字节。单个文件大小不超过16 MB。
- /// 取值为-1时,表示文件大小未知。文件上传完成时,需在上传文件分片的消息中,指定参数isComplete
- /// </summary>
- public long fileSize { get; set; }
- /// <summary>
- /// 物联网平台对设备上传同名文件的处理策略,默认为overwrite
- /// overwrite:覆盖模式 append:文件追加模式 reject:拒绝模式
- /// </summary>
- public string conflictStrategy { get; set; } = "overwrite";
- /// <summary>
- /// 文件的完整性校验模式,目前可取值crc64
- /// 若不传入,在文件上传完成后不校验文件完整性
- /// </summary>
- public string ficMode { get; set; }
- /// <summary>
- /// 文件的完整性校验值,是16位的Hex格式编码的字符串
- /// </summary>
- public string ficValue { get; set; }
- /// <summary>
- /// 自定义的设备请求上传文件的任务唯一ID,同一上传任务请求必须对应相同的唯一ID
- /// 不传入:物联网平台的云端识别当前请求为新的文件上传请求
- /// </summary>
- public string initUid { get; set; }
- /// <summary>
- /// 设备上传文件至OSS存储空间的配置参数
- /// </summary>
- public ConfigureParameters extraParams { get; set; } = new ConfigureParameters();
- }
- /// <summary>
- /// 配置参数
- /// </summary>
- public class ConfigureParameters
- {
- /// <summary>
- /// 设备上传文件的目标OSS所有者类型
- /// iot-platform:表示上传到阿里云物联网平台的OSS存储空间中
- /// device-user:表示上传到设备所属用户自己的OSS存储空间中
- /// </summary>
- public string ossOwnerType { get; set; } = "device-user";
- /// <summary>
- /// 文件上传相关的业务ID
- /// </summary>
- public string serviceId { get; set; }
- /// <summary>
- /// 标签
- /// </summary>
- public Dictionary<string, string> fileTag { get; set; }
- public ConfigureParameters()
- {
-
- }
- }
- #endregion
-
- #region 请求上传文件响应
- /// <summary>
- /// 请求响应
- /// </summary>
- public class FileUploadModelResult
- {
- /// <summary>
- /// 消息ID号
- /// 0~4294967295
- /// </summary>
- public string id { get; set; }
- /// <summary>
- /// 结果码。返回200表示成功
- /// </summary>
- public int code { get; set; }
- /// <summary>
- /// 请求失败时,返回的错误信息
- /// </summary>
- public string message { get; set; }
- /// <summary>
- /// 返回设备端的数据。
- /// </summary>
- public ResultData data { get; set; }
-
- }
- /// <summary>
- /// 请求响应数据
- /// </summary>
- public class ResultData
- {
- /// <summary>
- /// 设备上传文件的名称
- /// </summary>
- public string fileName { get; set; }
- /// <summary>
- /// 本次上传文件任务的标识ID。后续上传文件分片时,需要传递该文件标识ID。
- /// </summary>
- public string uploadId { get; set; }
- /// <summary>
- /// 仅当请求参数conflictStrategy为append,且物联网平台云端存在未完成上传的文件时,返回的已上传文件的大小,单位为字节。
- /// </summary>
- public long offset { get; set; }
- /// <summary>
- /// 当前上传文件分片的大小
- /// </summary>
- public long bSize { get; set; }
- /// <summary>
- /// 当上传了最后一个分片数据后,文件上传完成
- /// </summary>
- public bool complete { get; set; }
- /// <summary>
- /// 文件的完整性校验模式。若请求上传文件时传入了该参数,对应的值仅支持为crc64
- /// </summary>
- public string ficMode { get; set; }
- /// <summary>
- /// 文件上传完成,返回设备请求上传文件时的
- /// </summary>
- public string ficValueClient { get; set; }
- /// <summary>
- /// 文件上传完成,返回物联网平台云端计算的文件完整性校验值。该值与ficValueClient值相同,表示文件上传完整。
- /// </summary>
- public string ficValueServer { get; set; }
- }
- #endregion
-
- #region 上传文件
- public class FileSendModel
- {
- /// <summary>
- /// 消息ID号
- /// 0~4294967295
- /// </summary>
- public string id { get; set; }
- /// <summary>
- /// 上传业务参数
- /// </summary>
- public SendBusinessParameters @params { get; set; } = new SendBusinessParameters();
- public FileSendModel()
- {
- id = RandomHelper.GenerateRandomCode();
- }
- }
- /// <summary>
- /// 上传业务参数
- /// </summary>
- public class SendBusinessParameters
- {
- /// <summary>
- /// 设备请求上传文件时返回的文件上传任务标识ID。
- /// </summary>
- public string uploadId { get; set; }
- /// <summary>
- /// 已上传文件分片的总大小,单位为字节。
- /// </summary>
- public long offset { get; set; }
- /// <summary>
- /// 当前上传文件分片的大小,单位为字节
- /// </summary>
- public long bSize { get; set; }
- /// <summary>
- /// 仅当设备请求上传文件中fileSize为-1,即文件大小未知时,该参数有效,表示当前分片是否是文件的最后一个分片
- /// </summary>
- public bool isComplete { get; set; } = true;
- }
- #endregion
-
- #region 取消文件上传
- /// <summary>
- /// 取消上传
- /// </summary>
- public class CancelSend
- {
- /// <summary>
- /// 消息ID号
- /// 0~4294967295
- /// </summary>
- public string id { get; set; }
- /// <summary>
- /// 请求业务参数
- /// </summary>
- public object @params { get; set; }
-
- public CancelSend(string uploadId)
- {
- id = RandomHelper.GenerateRandomCode();
- @params = new { uploadId = uploadId };
- }
- }
- #endregion
-
- public class RandomHelper
- {
- /// <summary>
- ///生成制定位数的随机码(数字)
- /// </summary>
- /// <param name="length"></param>
- /// <returns></returns>
- public static string GenerateRandomCode(int length = 10)
- {
- var result = new StringBuilder();
- for (var i = 0; i < length; i++)
- {
- var r = new Random(Guid.NewGuid().GetHashCode());
- result.Append(r.Next(0, 10));
- }
- return result.ToString();
- }
- }
- }
|