diff --git a/插件库/Lskj.PubBomImport/FrmProgress.cs b/插件库/Lskj.PubBomImport/FrmProgress.cs index 0ccd2b8..286c9ed 100644 --- a/插件库/Lskj.PubBomImport/FrmProgress.cs +++ b/插件库/Lskj.PubBomImport/FrmProgress.cs @@ -3911,8 +3911,6 @@ namespace Lskj.PubBomImport string suffix = Path.GetExtension(File); string name = fileInfo.Name; DateTime creationTime = fileInfo.CreationTime; - byte[] fileBytes = ConvertFileToBytes(File); - string fileStream = Convert.ToBase64String(fileBytes); string userId = ERPInfo.Instance.UserId; DateTime uploadTime = DateTime.Now; string code = ""; @@ -3925,7 +3923,7 @@ namespace Lskj.PubBomImport //上传文件到服务器目录 //postUrl = "{0}Api/FileUploadApi.ashx?moduleId={1}&idValue={2}&speciesno={8}&folder={3}&totsize={4}&position={5}&filename={6}&method={7}&confirm=1"; - postUrl = "{0}Api/FileUploadApi.ashx?moduleId={1}&idValue={2}&speciesno={8}&folder={3}&totsize={4}&position={5}&filename={6}&method={7}&comfirm={9}&encode={10}&gzip=true&uploadToken={11}&__tsp={12}"; + postUrl = "{0}Api/FileUploadApi.ashx?moduleId={1}&idValue={2}&speciesno={8}&folder={3}&totsize={4}&position={5}&filename={6}&uname=false&method={7}&comfirm={9}&relPath=&encode={10}&gzip=true&uploadToken={11}&__tsp={12}"; if (!string.IsNullOrEmpty(idValue)) { @@ -3937,24 +3935,36 @@ namespace Lskj.PubBomImport loginPmsDic.Add("username", System.Web.HttpUtility.UrlEncode(ERPInfo.Instance.UserName)); loginPmsDic.Add("password", System.Web.HttpUtility.UrlEncode(ERPInfo.Instance.InPassWord)); HttpWebResponse loginResponse = HttpHelper.Post(loginUrl, "", loginPmsDic, null, out string loginResult); - if (loginResponse != null && !string.IsNullOrEmpty(loginResult)) + try { - JObject jObject = JsonConvert.DeserializeObject(loginResult); - if (jObject.ContainsKey("success")) + if (loginResponse != null && !string.IsNullOrEmpty(loginResult)) { - if ((jObject["success"] + "").Equals("True")) + JObject jObject = JsonConvert.DeserializeObject(loginResult); + if (jObject.ContainsKey("success")) { - if (jObject.ContainsKey("token")) + if ((jObject["success"] + "").Equals("True")) { - token = jObject["token"] + ""; + if (jObject.ContainsKey("token")) + { + token = jObject["token"] + ""; + } } } } } + finally + { + loginResponse?.Close(); + } if (!string.IsNullOrEmpty(token)) { - int pageSize = 1 * 1024 * 1024; long totalSize = fileInfo.Length; + // Web 前端上传器同样提示:经过防火墙的环境需要将分片 + // 从 1MB 降到约 0.3MB。仅大文件启用安全分片,避免影响 + // 现有小文件上传速度。 + int pageSize = totalSize > 4L * 1024 * 1024 + ? 320 * 1024 + : 1 * 1024 * 1024; long position = 0; int confirm = 1; int encode = 4; @@ -3963,7 +3973,7 @@ namespace Lskj.PubBomImport string uploadToken = Guid.NewGuid().ToString("N").Substring(0, 24); string uploadFileName = name; - string uploadMethod = "DoWebUpload"; + string uploadMethod = "DoWebUploadSafe"; Dictionary headerDic = new Dictionary(); headerDic.Add("Authorization", $"Bearer {token}"); @@ -3993,7 +4003,7 @@ namespace Lskj.PubBomImport string uploadUrl = string.Format( postUrl, SystemInfo.Instance.OAUrl, - "1000202", + "1000202-1", System.Web.HttpUtility.UrlEncode(idValue), "file", totalSize, @@ -4009,8 +4019,10 @@ namespace Lskj.PubBomImport string result = ""; HttpWebResponse webResponse = HttpHelper.PostFileChunk(uploadUrl, buffer, uploadFileName, headerDic, out result); + bool hasResponse = webResponse != null; + webResponse?.Close(); - if (webResponse != null && !string.IsNullOrEmpty(result)) + if (hasResponse && !string.IsNullOrEmpty(result)) { JObject jsonObject = (JObject)Newtonsoft.Json.JsonConvert.DeserializeObject(result); string isSuccess = jsonObject["success"] + ""; @@ -4079,7 +4091,9 @@ namespace Lskj.PubBomImport } else { - returnMsg = $"上传请求失败,{result}"; + long chunkIndex = position / pageSize + 1; + long chunkCount = (totalSize + pageSize - 1) / pageSize; + returnMsg = $"上传请求失败(分片{chunkIndex}/{chunkCount},位置{position},分片大小{pageSize / 1024}KB),{result}"; hasFail = true; isUpload = false; break; diff --git a/插件库/Lskj.Util/HttpHelper.cs b/插件库/Lskj.Util/HttpHelper.cs index a9b214a..c07af73 100644 --- a/插件库/Lskj.Util/HttpHelper.cs +++ b/插件库/Lskj.Util/HttpHelper.cs @@ -14,6 +14,7 @@ namespace Lskj.Util { private const int RequestTimeout = 120000; private const int Tls12 = 3072; + private const int UploadChunkMaxAttempts = 3; public enum Encode { @@ -101,6 +102,28 @@ namespace Lskj.Util return builder.ToString(); } + private static bool IsTransientUploadException(WebException ex) + { + if (ex == null) + { + return false; + } + + switch (ex.Status) + { + case WebExceptionStatus.ConnectFailure: + case WebExceptionStatus.ConnectionClosed: + case WebExceptionStatus.KeepAliveFailure: + case WebExceptionStatus.PipelineFailure: + case WebExceptionStatus.ReceiveFailure: + case WebExceptionStatus.SendFailure: + case WebExceptionStatus.Timeout: + return true; + default: + return false; + } + } + /// /// post数据到指定的网址,获取cookie数据,和返回页 /// @@ -270,70 +293,99 @@ namespace Lskj.Util /// public static HttpWebResponse PostFileChunk(string url, byte[] fileBytes, string fileName, Dictionary headerDic, out string result) { - HttpWebResponse httpWebResponse = null; - try + result = ""; + for (int attempt = 1; attempt <= UploadChunkMaxAttempts; attempt++) { - string boundary = "----WebKitFormBoundary" + DateTime.Now.Ticks.ToString("x"); - - ConfigureTransport(); - HttpWebRequest httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(url); - ConfigureRequest(httpWebRequest); - httpWebRequest.Method = "POST"; - httpWebRequest.Accept = Accept; - httpWebRequest.UserAgent = UserAgent; - httpWebRequest.ContentType = "multipart/form-data; boundary=" + boundary; - - if (headerDic != null) + HttpWebRequest httpWebRequest = null; + HttpWebResponse httpWebResponse = null; + try { - foreach (var item in headerDic) + string boundary = "----WebKitFormBoundary" + DateTime.Now.Ticks.ToString("x"); + + ConfigureTransport(); + httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(url); + ConfigureRequest(httpWebRequest); + // 客户网络中的代理/防火墙可能在复用连接时主动断开。 + // 分片请求使用独立连接,避免 ERP 进程内其他 HTTP 请求 + // 留下的连接状态影响当前上传。 + httpWebRequest.KeepAlive = false; + httpWebRequest.ConnectionGroupName = "LserpUpload-" + + Guid.NewGuid().ToString("N"); + httpWebRequest.ProtocolVersion = HttpVersion.Version11; + httpWebRequest.Method = "POST"; + httpWebRequest.Accept = Accept; + httpWebRequest.UserAgent = string.IsNullOrEmpty(UserAgent) + ? "Lserp-Desktop/6.0" + : UserAgent; + httpWebRequest.ContentType = "multipart/form-data; boundary=" + boundary; + + if (headerDic != null) { - if (!item.Key.Equals("Content-Type")) + foreach (var item in headerDic) { - httpWebRequest.Headers.Add(item.Key, item.Value); + if (!item.Key.Equals("Content-Type")) + { + httpWebRequest.Headers.Add(item.Key, item.Value); + } } } + + string header = + "--" + boundary + "\r\n" + + "Content-Disposition: form-data; name=\"file\"; filename=\"" + fileName + "\"\r\n" + + "Content-Type: application/octet-stream\r\n\r\n"; + + string footer = "\r\n--" + boundary + "--\r\n"; + byte[] headerBytes = Encoding.UTF8.GetBytes(header); + byte[] footerBytes = Encoding.UTF8.GetBytes(footer); + + httpWebRequest.ContentLength = headerBytes.Length + fileBytes.Length + footerBytes.Length; + + using (Stream stream = httpWebRequest.GetRequestStream()) + { + stream.Write(headerBytes, 0, headerBytes.Length); + stream.Write(fileBytes, 0, fileBytes.Length); + stream.Write(footerBytes, 0, footerBytes.Length); + } + + httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse(); + + using (Stream responseStream = httpWebResponse.GetResponseStream()) + using (StreamReader streamReader = new StreamReader(responseStream, Encoding.UTF8)) + { + result = streamReader.ReadToEnd(); + } + + return httpWebResponse; } + catch (WebException ex) + { + Console.WriteLine(ex.ToString()); + string exceptionMessage = BuildWebExceptionMessage(ex); + httpWebResponse?.Close(); + ex.Response?.Close(); + httpWebRequest?.Abort(); - string header = - "--" + boundary + "\r\n" + - "Content-Disposition: form-data; name=\"file\"; filename=\"" + fileName + "\"\r\n" + - "Content-Type: application/octet-stream\r\n\r\n"; + if (attempt < UploadChunkMaxAttempts && IsTransientUploadException(ex)) + { + System.Threading.Thread.Sleep(attempt * 1000); + continue; + } - string footer = "\r\n--" + boundary + "--\r\n"; - - byte[] headerBytes = Encoding.UTF8.GetBytes(header); - byte[] footerBytes = Encoding.UTF8.GetBytes(footer); - - httpWebRequest.ContentLength = headerBytes.Length + fileBytes.Length + footerBytes.Length; - - Stream stream = httpWebRequest.GetRequestStream(); - stream.Write(headerBytes, 0, headerBytes.Length); - stream.Write(fileBytes, 0, fileBytes.Length); - stream.Write(footerBytes, 0, footerBytes.Length); - stream.Close(); - - httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse(); - - Stream responseStream = httpWebResponse.GetResponseStream(); - StreamReader streamReader = new StreamReader(responseStream, Encoding.UTF8); - result = streamReader.ReadToEnd(); - streamReader.Close(); - responseStream.Close(); - - return httpWebResponse; - } - catch (WebException ex) - { - Console.WriteLine(ex.ToString()); - result = BuildWebExceptionMessage(ex); - return httpWebResponse; - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - result = BuildExceptionMessage(ex); - return httpWebResponse; + result = string.Format("{0}; Attempts={1}", exceptionMessage, attempt); + return null; + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + httpWebResponse?.Close(); + httpWebRequest?.Abort(); + result = BuildExceptionMessage(ex); + return null; + } } + + return null; } ///