using BPA.Helper; 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 { /// /// 阿里云文件上传 /// 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"; /// /// 一次性上传的包 /// public static byte[] FileBlock; /// /// 请求反馈信息 /// public static FileUploadModelResult modelResult = null; /// /// 文件请求上传 /// public static void FileRequest(DataVReport dataV) { TaskManage.GetInstance.Start(new Action(() => { try { List 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 { {"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); } /// /// 文件上传 /// 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 message = new List() { HeaderLen_H, HeaderLen_L }; message.AddRange(Header); message.AddRange(FileBlock); message.AddRange(CRC16); dataV.IOT_Publish(dataV.FileUpLoadSendTopic, message.ToArray()); } /// /// 取消文件上传 /// public static void FileCancelSend(DataVReport dataV, string uploadId) { CancelSend cancel = new CancelSend(uploadId); dataV.IOT_Publish(dataV.CancelFileUpLoadSendTopic, Tools.JsonConvertTools(cancel)); } /// /// 读取文件 /// /// /// 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]; } } } /// /// 读取文件字节 /// /// /// 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 请求上传文件 /// /// 请求上传文件 /// public class FileUploadModel { /// /// 消息ID号 /// 0~4294967295 /// public string id { get; set; } /// /// 请求业务参数 /// public BusinessParameters @params { get; set; } public FileUploadModel() { id = RandomHelper.GenerateRandomCode(); @params = new BusinessParameters(); } } /// /// 业务参数 /// public class BusinessParameters { /// /// 设备上传文件的名称 /// public string fileName { get; set; } /// /// 上传文件大小,单位字节。单个文件大小不超过16 MB。 /// 取值为-1时,表示文件大小未知。文件上传完成时,需在上传文件分片的消息中,指定参数isComplete /// public long fileSize { get; set; } /// /// 物联网平台对设备上传同名文件的处理策略,默认为overwrite /// overwrite:覆盖模式 append:文件追加模式 reject:拒绝模式 /// public string conflictStrategy { get; set; } = "overwrite"; /// /// 文件的完整性校验模式,目前可取值crc64 /// 若不传入,在文件上传完成后不校验文件完整性 /// public string ficMode { get; set; } /// /// 文件的完整性校验值,是16位的Hex格式编码的字符串 /// public string ficValue { get; set; } /// /// 自定义的设备请求上传文件的任务唯一ID,同一上传任务请求必须对应相同的唯一ID /// 不传入:物联网平台的云端识别当前请求为新的文件上传请求 /// public string initUid { get; set; } /// /// 设备上传文件至OSS存储空间的配置参数 /// public ConfigureParameters extraParams { get; set; } = new ConfigureParameters(); } /// /// 配置参数 /// public class ConfigureParameters { /// /// 设备上传文件的目标OSS所有者类型 /// iot-platform:表示上传到阿里云物联网平台的OSS存储空间中 /// device-user:表示上传到设备所属用户自己的OSS存储空间中 /// public string ossOwnerType { get; set; } = "device-user"; /// /// 文件上传相关的业务ID /// public string serviceId { get; set; } /// /// 标签 /// public Dictionary fileTag { get; set; } public ConfigureParameters() { } } #endregion #region 请求上传文件响应 /// /// 请求响应 /// public class FileUploadModelResult { /// /// 消息ID号 /// 0~4294967295 /// public string id { get; set; } /// /// 结果码。返回200表示成功 /// public int code { get; set; } /// /// 请求失败时,返回的错误信息 /// public string message { get; set; } /// /// 返回设备端的数据。 /// public ResultData data { get; set; } } /// /// 请求响应数据 /// public class ResultData { /// /// 设备上传文件的名称 /// public string fileName { get; set; } /// /// 本次上传文件任务的标识ID。后续上传文件分片时,需要传递该文件标识ID。 /// public string uploadId { get; set; } /// /// 仅当请求参数conflictStrategy为append,且物联网平台云端存在未完成上传的文件时,返回的已上传文件的大小,单位为字节。 /// public long offset { get; set; } /// /// 当前上传文件分片的大小 /// public long bSize { get; set; } /// /// 当上传了最后一个分片数据后,文件上传完成 /// public bool complete { get; set; } /// /// 文件的完整性校验模式。若请求上传文件时传入了该参数,对应的值仅支持为crc64 /// public string ficMode { get; set; } /// /// 文件上传完成,返回设备请求上传文件时的 /// public string ficValueClient { get; set; } /// /// 文件上传完成,返回物联网平台云端计算的文件完整性校验值。该值与ficValueClient值相同,表示文件上传完整。 /// public string ficValueServer { get; set; } } #endregion #region 上传文件 public class FileSendModel { /// /// 消息ID号 /// 0~4294967295 /// public string id { get; set; } /// /// 上传业务参数 /// public SendBusinessParameters @params { get; set; } = new SendBusinessParameters(); public FileSendModel() { id = RandomHelper.GenerateRandomCode(); } } /// /// 上传业务参数 /// public class SendBusinessParameters { /// /// 设备请求上传文件时返回的文件上传任务标识ID。 /// public string uploadId { get; set; } /// /// 已上传文件分片的总大小,单位为字节。 /// public long offset { get; set; } /// /// 当前上传文件分片的大小,单位为字节 /// public long bSize { get; set; } /// /// 仅当设备请求上传文件中fileSize为-1,即文件大小未知时,该参数有效,表示当前分片是否是文件的最后一个分片 /// public bool isComplete { get; set; } = true; } #endregion #region 取消文件上传 /// /// 取消上传 /// public class CancelSend { /// /// 消息ID号 /// 0~4294967295 /// public string id { get; set; } /// /// 请求业务参数 /// public object @params { get; set; } public CancelSend(string uploadId) { id = RandomHelper.GenerateRandomCode(); @params = new { uploadId = uploadId }; } } #endregion public class RandomHelper { /// ///生成制定位数的随机码(数字) /// /// /// 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(); } } }