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();
}
}
}