using BZFAStandardLib; using Newtonsoft.Json; using Sunny.UI.Win32; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Http; using System.Security.Cryptography; using System.Security.Policy; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using static MainForm.ClassFile.XiaomiAPI.XiaomiMqttClient_Extend; using static MainForm.ClassFile.XiaomiAPI_MES.XiaomiMESHttp_StationInbound; using static MainForm.ClassFile.XiaomiAPI_MES.XiaomiMESHttp_StationOutbound.XmMES_StationOutRequest_Body; using static MainForm.ClassFile.XiaomiClass.MesHelper; namespace MainForm.ClassFile.XiaomiAPI_MES { /// /// 小米MES - 进站接⼝ /// 接口地址: /// 接口方法:UnitConfirmDataSetIn /// public class XiaomiMESHttp_UpLoadFile : XiaomiMESHttp_X5 { #region 变量 /// /// 接口地址 /// protected new static string UpFileUrl { set; get; } = GlobalContext.UpFileUrl; /// /// 接口方法 /// protected new static string Method { set; get; } = "UploadMqtt"; #endregion 变量 /// /// 文件上传到 MES 系统 /// /// 文件路径 /// 文件上传参数 /// MQTT 负载参数 /// (状态码, 结果信息) public static async Task<(int, string)> FileUoladToMes(string wJPath, FileUpload_X5 fileUpload_X5, FileMqttPayload fileMqttPayload) { string logPath = GlobalContext.MqttFileUpLogDir + "FileInfo" + DateTime.Now.ToString("yyyyMMdd") + ".txt"; try { Stopwatch stopwatch1 = new Stopwatch(); // 基础参数 //string url = "http://cm.pre.mi.com/file/x5/file/upload/mqtt"; //url = "http://im.pre.mi.com/file/x5/file/upload/mqtt"; string url = GlobalContext.UpFileUrl; //这个之后要加到配置文件config中 string appid = GlobalContext.FileAppId; //这个之后要加到配置文件config中 string appkey = GlobalContext.FileAppKey; string method = "UploadMqtt"; // 检查文件是否存在 if (!System.IO.File.Exists(wJPath)) { return (-1, "文件不存在"); } // 获取文件信息 FileInfo file = new FileInfo(wJPath); // 构造 body string body = BuildBody(fileUpload_X5, fileMqttPayload); // 计算 sign string sign = GetSign(appid + body + appkey); // MD5 加密 // 构造 header Dictionary header = BuildHeader(url, appid, method, sign); // 构造 data 参数 Dictionary dataParam = new Dictionary { { "header", header }, { "body", body } }; // 将 data 参数序列化为 Base64 编码的字符串 string data = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(dataParam))); FileOperate.NewTxtFile(logPath, DateTime.Now + $"==>【文件提交】开始上传:文件包[{fileUpload_X5.bucket}],文件名[{file.Name}]"); stopwatch1.Start(); // 调用上传方法 var uploadResult = UploadFile(url, file, data).Result; stopwatch1.Stop(); FileOperate.NewTxtFile(logPath, DateTime.Now+"===>【文件提交】HTTP提交:" + JsonConvert.SerializeObject(dataParam) + "\r\n==>提交结果:" + uploadResult + ""); //获取返回参数 FileUpload_Result fileUpList = JsonConvert.DeserializeObject(uploadResult); // 判断上传结果 if (!string.IsNullOrEmpty(uploadResult) && !uploadResult.StartsWith("异常:") && !uploadResult.StartsWith("HTTP 错误:")) { //附件信息添加到主页面 FileData fileData = new FileData(); fileData.FileName = file.Name; fileData.FileId = fileUpList.body.uuid; fileData.Bucket = fileUpList.body.bucket; Form_Home.fileUploadData.fileData.Add(fileData); FileOperate.NewTxtFile(logPath, DateTime.Now + $"==>【文件提交】上传结束:文件包[{fileUpload_X5.bucket}],文件名[{file.Name}],耗时[{stopwatch1.ElapsedMilliseconds}]"); return (1, fileUpload_X5.name + $"文件上传成功"); } else { FileOperate.NewTxtFile(logPath, DateTime.Now + $"==>【文件提交】上传失败:文件包[{fileUpload_X5.bucket}],文件名[{file.Name}],耗时[{stopwatch1.ElapsedMilliseconds}]"); return (-2, fileUpload_X5.name+$"文件上传失败: {uploadResult}"); } } catch (Exception ex) { return (-3, fileUpload_X5.name+$"发生异常: {ex.Message}"); } } /// /// 将文件复制到目标文件夹,并删除源文件 /// /// 源文件路径 /// 目标文件夹路径 static string CopyAndDeleteFile(string sourceFilePath, string destinationFolderPath) { try { // 检查目标文件夹是否存在,如果不存在则创建 if (!Directory.Exists(destinationFolderPath)) { Directory.CreateDirectory(destinationFolderPath); } // 构建目标文件的完整路径 string fileName = Path.GetFileName(sourceFilePath); // 获取源文件的文件名 string destinationFilePath = Path.Combine(destinationFolderPath, fileName); // 如果目标文件已存在,则删除已有文件以避免冲突 if (File.Exists(destinationFilePath)) { File.Delete(destinationFilePath); // 删除已有文件 } // 检查文件是否准备好 if (!IsFileReady(sourceFilePath)) { throw new IOException($"文件 {sourceFilePath} 正被其他进程占用,无法进行复制和删除操作。"); } // 复制文件到目标文件夹 File.Copy(sourceFilePath, destinationFilePath); // 删除源文件 File.Delete(sourceFilePath); Console.WriteLine($"文件已从 {sourceFilePath} 成功复制到 {destinationFilePath} 并删除原文件。"); // 返回目标文件路径 return destinationFilePath; } catch (Exception ex) { Console.WriteLine($"文件转移失败: {ex.Message}"); return null; // 或者返回空字符串,根据需求决定 } } /// /// 删除文件 /// /// 文件路径 public static (bool,string) DeleteFile(string sourceFilePath) { try { // 构建目标文件的完整路径 string fileName = Path.GetFileName(sourceFilePath); // 获取源文件的文件名 // 检查文件是否准备好 if (!IsFileReady(sourceFilePath)) { throw new IOException($"文件 {sourceFilePath} 正被其他进程占用,无法进行复制和删除操作。"); } // 删除源文件 File.Delete(sourceFilePath); return (true, "文件删除成功!"); } catch (Exception ex) { return (false, "文件删除失败!"+ex.Message); } } public static bool IsFileReady(string filePath) { try { using (FileStream stream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { stream.Close(); } return true; } catch (IOException) { return false; } } /// /// 文件MD5加密 /// /// /// public static string GetMD5Hex(FileInfo file) { using (MD5 md5 = MD5.Create()) { using (FileStream stream = file.OpenRead()) { byte[] hashBytes = md5.ComputeHash(stream); StringBuilder sb = new StringBuilder(); for (int i = 0; i < hashBytes.Length; i++) { sb.Append(hashBytes[i].ToString("x2")); } return sb.ToString(); } } } /// /// 数据pin接 /// /// /// /// public static string BuildBody(FileUpload_X5 file, FileMqttPayload payload) { Dictionary fileMetadata = new Dictionary(); Dictionary mqttPayLoad = new Dictionary(); mqttPayLoad.Add("factory", payload.factory); mqttPayLoad.Add("project_name", payload.project_name); mqttPayLoad.Add("product_mode", payload.product_mode); mqttPayLoad.Add("line_no", payload.line_no); mqttPayLoad.Add("work_station_no", payload.work_station_no); mqttPayLoad.Add("equipment_no", payload.equipment_no); mqttPayLoad.Add("station_no", payload.station_no); mqttPayLoad.Add("file_id", payload.file_id); mqttPayLoad.Add("file_name", payload.file_name); mqttPayLoad.Add("sn", payload.sn); mqttPayLoad.Add("opt_time", payload.opt_time); mqttPayLoad.Add("file_type", payload.file_type); mqttPayLoad.Add("file_category", payload.file_category); mqttPayLoad.Add("tag", payload.tag); mqttPayLoad.Add("reference_info", Newtonsoft.Json.JsonConvert.SerializeObject(new object[] { new { pass_station_id = payload.reference_info.pass_station_id } })); fileMetadata.Add("bucket", file.bucket); fileMetadata.Add("name", file.name); fileMetadata.Add("uuid", file.uuid); fileMetadata.Add("md5", file.md5); fileMetadata.Add("uploadCloud", file.uploadCloud); fileMetadata.Add("informMqtt", file.informMqtt); fileMetadata.Add("mqttPayload", Newtonsoft.Json.JsonConvert.SerializeObject(mqttPayLoad)); string body = Newtonsoft.Json.JsonConvert.SerializeObject(fileMetadata); return body; } /// /// 数据头拼接 /// /// /// /// /// /// public static Dictionary BuildHeader(string url, string appid, string method, string sign) { Dictionary header = new Dictionary(); header.Add("appid", appid); header.Add("method", method); header.Add("sign", sign); header.Add("url", url); return header; } public static string GetGuid() { return (System.Guid.NewGuid().ToString("N")); } /// /// 异步文件上传 /// /// 上传地址 /// 文件路径 /// 上传的数据 /// 返回上传结果 public static async Task UploadFile(string url, FileInfo file, string data) { using (var httpClient = new HttpClient()) { //httpClient.Timeout = new TimeSpan(80000000); var formData = new MultipartFormDataContent(); // 添加文件 var fileContent = new StreamContent(file.OpenRead()); formData.Add(fileContent, "file", file.Name); // 添加数据 formData.Add(new StringContent(data), "data"); try { //var response = await httpClient.PostAsync(url, formData); var response = httpClient.PostAsync(url, formData).Result; if (response.IsSuccessStatusCode) { return await response.Content.ReadAsStringAsync(); } else { return $"HTTP 错误: {response.StatusCode}"; } } catch (Exception e) { return $"异常: {e.Message}"; } } } /// /// MD5加密 /// /// /// public static string GetSign(string data) { // 实例化一个md5对像 MD5 md5 = MD5.Create(); // MD5加密 byte[] encodingMd5Data = md5.ComputeHash(Encoding.UTF8.GetBytes(data)); // 生成签名字段 string sign = ""; for (int i = 0; i < encodingMd5Data.Length; i++) { // 将得到的字符串使用十六进制类型格式。格式后的字符是小写的字母,如果使用大写(X)则格式后的字符是大写字符 sign += encodingMd5Data[i].ToString("X2"); } return sign; } public class FileUpload_X5 { /// /// ⽂件所属包 /// 必填 /* ${file_category}/${file_type}/${项⽬号}/${⽣ 产阶段}/${运⾏模式}/${过站结果}/${装备编 码}/${sn}/${pass_station_id} • 其中file_category的枚举值为: ◦ IMAGE ◦ TEXT • 若对应字段的值为空,则使⽤默认值 UNKNOWN 注意:⾸位不能出现/,否则会出现路径错误 问题。 如: 正确⽰例 IMAGE/IMAGE/N3/debug/online/PASS/MPA-0001/P320N000006B/382f55e9-c2bb */ /// public string bucket { get; set; } = string.Empty; /// /// 文件名 /// public string name { get; set; } = string.Empty; /// /// 文件唯一标识符 /// public string uuid { get; set; } = string.Empty; /// /// md5 传空 /// public string md5 { get; set; } = string.Empty; /// /// 是否上云 默认true /// public Boolean uploadCloud { get; set; } /// /// 是否通知Mqtt 默认true /// public Boolean informMqtt { get; set; } } public class FileMqttPayload { /// /// 工厂编码 /// public string factory { get; set; } = string.Empty; /// /// 项目号 /// public string project_name { get; set; } = string.Empty; /// /// 生产阶段 /// public string product_mode { get; set; } = string.Empty; /// /// 线体 /// public string line_no { get; set; } = string.Empty; /// /// 工站 /// public string work_station_no { get; set; } = string.Empty; /// /// 装备 /// public string equipment_no { get; set; } = string.Empty; /// /// 工位 /// public string station_no { get; set; } = string.Empty; /// /// 文件ID /// public string file_id { get; set; } = string.Empty; /// /// 文件名 /// public string file_name { get; set; } = string.Empty; /// /// 产品sn /// public string sn { get; set; } = string.Empty; /// /// 文件生成时间 /// public string opt_time { get; set; } = string.Empty; /// /// 文件类型 /// public string file_type { get; set; } = string.Empty; /// /// 文件类别 /// public string file_category { get; set; } = string.Empty; /// /// 自定义标签信息 /// public string tag { get; set; } = string.Empty; /// /// 关联业务信息 /// public Reference_Info reference_info { get; set; }=new Reference_Info(); } public class Reference_Info { public string pass_station_id { get; set; } = string.Empty; } /// /// 上传文件-返回参数 /// public struct FileUpload_Result { public Header header { get; set; } public Body body { get; set; } public struct Header { public string code { get; set; } public string desc { get; set; } } public struct Body { public string uuid { get; set; } public string md5 { get; set; } public string bucket { get; set; } } } /// /// 待上传MES、Iot的文件信息 /// public class FileUpload_FileData { public List fileData { get; set; } = new List(); } public class FileData { /// /// ⽂件名称 /// public string FileName { get; set; } = string.Empty; /// /// ⽂件在⽂件服务器对应的uuid,可以采⽤异步上传,⾃定义uuid,先上传out,然后再异步上传⽂件,提升过站效率 /// public string FileId { get; set; } = string.Empty; /// /// ⽂件服务器bucket /// public string Bucket { get; set; } = string.Empty; } } }