fix: optimize large file chunk upload stability
This commit is contained in:
+106
-54
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// post数据到指定的网址,获取cookie数据,和返回页
|
||||
/// </summary>
|
||||
@@ -270,70 +293,99 @@ namespace Lskj.Util
|
||||
/// <returns></returns>
|
||||
public static HttpWebResponse PostFileChunk(string url, byte[] fileBytes, string fileName, Dictionary<string, string> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user