培训考核三期,新版培训,网页版培训登录器
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

1262 lines
53 KiB

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Text;
using BestHTTP;
using Newtonsoft.Json;
using System.IO;
using Cysharp.Threading.Tasks;
using System.Security.Cryptography;
using System.Linq;
using AX.MessageSystem;
/// <summary>
/// 服务器配置文件对应类型
/// </summary>
public class ServerConfig
{
public string Ip;
public string Port;
}
/// <summary>
/// 刷新令牌信息。
/// </summary>
public class RefreshTokenInfo
{
/// <summary>
/// JWT令牌。
/// </summary>
public string Token { get; set; }
/// <summary>
/// 刷新令牌。
/// </summary>
public string RefreshToken { get; set; }
}
public class HTTPRequestManager : MonoBehaviour
{
public static HTTPRequestManager Instance;
private string baseHttpUrl = "";
public IdentityInfo identityInfo = new IdentityInfo(); //记录登录成功后的身份信息
public string userName;//记录登录成功后的用户名
private const string refreshTokenApi = "/api/Account/RefreshToken";
#region 文件下载、上传成员变量
private const int fragmentSize = 5 * 1024 * 1024;//分块大小
private const string newMultipartUploadApi = "/api/NewMultipartUpload/firetraining";
private const string completeMultipartUploadApi = "/api/CompleteMultipartUpload/firetraining";
private const string multipartUploadInfosKey = "MultipartUploadInfos";
#endregion
private void Awake()
{
DontDestroyOnLoad(this);
Instance = this;
//Json序列化全局默认设置
JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
{
Formatting = Formatting.Indented
};
}
void Start()
{
if (string.IsNullOrEmpty(baseHttpUrl))
{
string filepath = Application.streamingAssetsPath + @"/ServerConfig.json";
LoadFileHelper.Instance.LoadFileStringAsync(filepath, (s) =>
{
ServerConfig config = JsonConvert.DeserializeObject<ServerConfig>(s);
baseHttpUrl = "http://" + config.Ip + ":" + config.Port;
MessageDispatcher.SendMessage("ServerConfigLoaded");
});
}
}
/// <summary>
/// 获取json数据
/// </summary>
/// <typeparam name="T">返回数据类型</typeparam>
/// <param name="apiUrl">API URL</param>
/// <param name="success">请求成功回调</param>
/// <param name="error">请求错误回调</param>
public void GetJson<T>(string apiUrl, Action<T> success, Action<int, string> error = null)
{
if (string.IsNullOrEmpty(apiUrl))
{
throw new ArgumentNullException($"API URL:{apiUrl}无效!");
}
HTTPRequest request = new HTTPRequest(new Uri(baseHttpUrl + apiUrl), HTTPMethods.Get, (req, resp) =>
{
switch (req.State)
{
case HTTPRequestStates.Finished:
if (resp.IsSuccess)
{
T result = JsonConvert.DeserializeObject<T>(resp.DataAsText);
success?.Invoke(result);
}
else
{
Debug.LogWarning(string.Format("Request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}",
resp.StatusCode,
resp.Message,
resp.DataAsText));
error?.Invoke(resp.StatusCode, JsonConvert.DeserializeObject<ProblemDetails>(resp.DataAsText).detail);
}
break;
case HTTPRequestStates.Error:
Debug.LogError("Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception"));
error?.Invoke((int)HTTPRequestStates.Error, "Request Finished with Error!");
break;
case HTTPRequestStates.Aborted:
Debug.LogWarning("Request Aborted!");
error?.Invoke((int)HTTPRequestStates.Aborted, "Request Aborted!");
break;
case HTTPRequestStates.ConnectionTimedOut:
Debug.LogError("Connection Timed Out!");
error?.Invoke((int)HTTPRequestStates.ConnectionTimedOut, "Connection Timed Out!");
break;
case HTTPRequestStates.TimedOut:
Debug.LogError("Processing the request Timed Out!");
error?.Invoke((int)HTTPRequestStates.TimedOut, "Processing the request Timed Out!");
break;
}
});
request.SetHeader("Authorization", "Bearer " + identityInfo.token);
request.Send();
}
/// <summary>
/// 提交json数据
/// </summary>
/// <typeparam name="TIn">提交数据类型</typeparam>
/// <typeparam name="TOut">返回数据类型</typeparam>
/// <param name="apiUrl">API URL</param>
/// <param name="data">提交数据</param>
/// <param name="success">请求成功回调</param>
/// <param name="error">请求错误回调</param>
public void PostJson<TIn, TOut>(string apiUrl, TIn data, Action<TOut> success, Action<int, string> error = null)
{
if (string.IsNullOrEmpty(apiUrl))
{
throw new ArgumentNullException($"API URL:{apiUrl}无效!");
}
HTTPRequest request = new HTTPRequest(new Uri(baseHttpUrl + apiUrl), HTTPMethods.Post, (req, resp) =>
{
switch (req.State)
{
case HTTPRequestStates.Finished:
if (resp.IsSuccess)
{
TOut result = JsonConvert.DeserializeObject<TOut>(resp.DataAsText);
success?.Invoke(result);
}
else
{
Debug.LogWarning(string.Format("Request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}",
resp.StatusCode,
resp.Message,
resp.DataAsText));
error?.Invoke(resp.StatusCode, JsonConvert.DeserializeObject<ProblemDetails>(resp.DataAsText).detail);
}
break;
case HTTPRequestStates.Error:
Debug.LogError("Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception"));
error?.Invoke((int)HTTPRequestStates.Error, "Request Finished with Error!");
break;
case HTTPRequestStates.Aborted:
Debug.LogWarning("Request Aborted!");
error?.Invoke((int)HTTPRequestStates.Aborted, "Request Aborted!");
break;
case HTTPRequestStates.ConnectionTimedOut:
Debug.LogError("Connection Timed Out!");
error?.Invoke((int)HTTPRequestStates.ConnectionTimedOut, "Connection Timed Out!");
break;
case HTTPRequestStates.TimedOut:
Debug.LogError("Processing the request Timed Out!");
error?.Invoke((int)HTTPRequestStates.TimedOut, "Processing the request Timed Out!");
break;
}
});
request.SetHeader("Authorization", "Bearer " + identityInfo.token);
request.SetHeader("Content-Type", "application/json; charset=UTF-8");
string json = JsonConvert.SerializeObject(data);
request.RawData = Encoding.UTF8.GetBytes(json);
request.Send();
}
/// <summary>
/// 提交json数据
/// </summary>
/// <typeparam name="TIn">提交数据类型</typeparam>
/// <param name="apiUrl">API URL</param>
/// <param name="data">提交数据</param>
/// <param name="success">请求成功回调</param>
/// <param name="error">请求错误回调</param>
public void PostJson<TIn>(string apiUrl, TIn data, Action success, Action<int, string> error = null)
{
if (string.IsNullOrEmpty(apiUrl))
{
throw new ArgumentNullException($"API URL:{apiUrl}无效!");
}
HTTPRequest request = new HTTPRequest(new Uri(baseHttpUrl + apiUrl), HTTPMethods.Post, (req, resp) =>
{
switch (req.State)
{
case HTTPRequestStates.Finished:
if (resp.IsSuccess)
{
success?.Invoke();
}
else
{
Debug.LogWarning(string.Format("Request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}",
resp.StatusCode,
resp.Message,
resp.DataAsText));
error?.Invoke(resp.StatusCode, JsonConvert.DeserializeObject<ProblemDetails>(resp.DataAsText).detail);
}
break;
case HTTPRequestStates.Error:
Debug.LogError("Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception"));
error?.Invoke((int)HTTPRequestStates.Error, "Request Finished with Error!");
break;
case HTTPRequestStates.Aborted:
Debug.LogWarning("Request Aborted!");
error?.Invoke((int)HTTPRequestStates.Aborted, "Request Aborted!");
break;
case HTTPRequestStates.ConnectionTimedOut:
Debug.LogError("Connection Timed Out!");
error?.Invoke((int)HTTPRequestStates.ConnectionTimedOut, "Connection Timed Out!");
break;
case HTTPRequestStates.TimedOut:
Debug.LogError("Processing the request Timed Out!");
error?.Invoke((int)HTTPRequestStates.TimedOut, "Processing the request Timed Out!");
break;
}
});
request.SetHeader("Authorization", "Bearer " + identityInfo.token);
request.SetHeader("Content-Type", "application/json; charset=UTF-8");
string json = JsonConvert.SerializeObject(data);
request.RawData = Encoding.UTF8.GetBytes(json);
request.Send();
}
/// <summary>
/// 提交json数据
/// </summary>
/// <typeparam name="TOut">返回数据类型</typeparam>
/// <param name="apiUrl">API URL</param>
/// <param name="success">请求成功回调</param>
/// <param name="error">请求错误回调</param>
public void PostJson<TOut>(string apiUrl, Action<TOut> success, Action<int, string> error = null)
{
if (string.IsNullOrEmpty(apiUrl))
{
throw new ArgumentNullException($"API URL:{apiUrl}无效!");
}
HTTPRequest request = new HTTPRequest(new Uri(baseHttpUrl + apiUrl), HTTPMethods.Post, (req, resp) =>
{
switch (req.State)
{
case HTTPRequestStates.Finished:
if (resp.IsSuccess)
{
TOut result = JsonConvert.DeserializeObject<TOut>(resp.DataAsText);
success?.Invoke(result);
}
else
{
Debug.LogWarning(string.Format("Request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}",
resp.StatusCode,
resp.Message,
resp.DataAsText));
error?.Invoke(resp.StatusCode, JsonConvert.DeserializeObject<ProblemDetails>(resp.DataAsText).detail);
}
break;
case HTTPRequestStates.Error:
Debug.LogError("Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception"));
error?.Invoke((int)HTTPRequestStates.Error, "Request Finished with Error!");
break;
case HTTPRequestStates.Aborted:
Debug.LogWarning("Request Aborted!");
error?.Invoke((int)HTTPRequestStates.Aborted, "Request Aborted!");
break;
case HTTPRequestStates.ConnectionTimedOut:
Debug.LogError("Connection Timed Out!");
error?.Invoke((int)HTTPRequestStates.ConnectionTimedOut, "Connection Timed Out!");
break;
case HTTPRequestStates.TimedOut:
Debug.LogError("Processing the request Timed Out!");
error?.Invoke((int)HTTPRequestStates.TimedOut, "Processing the request Timed Out!");
break;
}
});
request.SetHeader("Authorization", "Bearer " + identityInfo.token);
request.Send();
}
/// <summary>
/// 提交json数据
/// </summary>
/// <param name="apiUrl">API URL</param>
/// <param name="success">请求成功回调</param>
/// <param name="error">请求错误回调</param>
public void PostJson(string apiUrl, Action success, Action<int, string> error = null)
{
if (string.IsNullOrEmpty(apiUrl))
{
throw new ArgumentNullException($"API URL:{apiUrl}无效!");
}
HTTPRequest request = new HTTPRequest(new Uri(baseHttpUrl + apiUrl), HTTPMethods.Post, (req, resp) =>
{
switch (req.State)
{
case HTTPRequestStates.Finished:
if (resp.IsSuccess)
{
success?.Invoke();
}
else
{
Debug.LogWarning(string.Format("Request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}",
resp.StatusCode,
resp.Message,
resp.DataAsText));
error?.Invoke(resp.StatusCode, JsonConvert.DeserializeObject<ProblemDetails>(resp.DataAsText).detail);
}
break;
case HTTPRequestStates.Error:
Debug.LogError("Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception"));
error?.Invoke((int)HTTPRequestStates.Error, "Request Finished with Error!");
break;
case HTTPRequestStates.Aborted:
Debug.LogWarning("Request Aborted!");
error?.Invoke((int)HTTPRequestStates.Aborted, "Request Aborted!");
break;
case HTTPRequestStates.ConnectionTimedOut:
Debug.LogError("Connection Timed Out!");
error?.Invoke((int)HTTPRequestStates.ConnectionTimedOut, "Connection Timed Out!");
break;
case HTTPRequestStates.TimedOut:
Debug.LogError("Processing the request Timed Out!");
error?.Invoke((int)HTTPRequestStates.TimedOut, "Processing the request Timed Out!");
break;
}
});
request.SetHeader("Authorization", "Bearer " + identityInfo.token);
request.Send();
}
/// <summary>
/// 修改json数据
/// </summary>
/// <typeparam name="TIn">修改数据类型</typeparam>
/// <typeparam name="TOut">返回数据类型</typeparam>
/// <param name="apiUrl">API URL</param>
/// <param name="data">修改数据</param>
/// <param name="success">请求成功回调</param>
/// <param name="error">请求错误回调</param>
public void PatchJson<TIn, TOut>(string apiUrl, TIn data, Action<TOut> success, Action<int, string> error = null)
{
if (string.IsNullOrEmpty(apiUrl))
{
throw new ArgumentNullException($"API URL:{apiUrl}无效!");
}
HTTPRequest request = new HTTPRequest(new Uri(baseHttpUrl + apiUrl), HTTPMethods.Patch, (req, resp) =>
{
switch (req.State)
{
case HTTPRequestStates.Finished:
if (resp.IsSuccess)
{
TOut result = JsonConvert.DeserializeObject<TOut>(resp.DataAsText);
success?.Invoke(result);
}
else
{
Debug.LogWarning(string.Format("Request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}",
resp.StatusCode,
resp.Message,
resp.DataAsText));
error?.Invoke(resp.StatusCode, JsonConvert.DeserializeObject<ProblemDetails>(resp.DataAsText).detail);
}
break;
case HTTPRequestStates.Error:
Debug.LogError("Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception"));
error?.Invoke((int)HTTPRequestStates.Error, "Request Finished with Error!");
break;
case HTTPRequestStates.Aborted:
Debug.LogWarning("Request Aborted!");
error?.Invoke((int)HTTPRequestStates.Aborted, "Request Aborted!");
break;
case HTTPRequestStates.ConnectionTimedOut:
Debug.LogError("Connection Timed Out!");
error?.Invoke((int)HTTPRequestStates.ConnectionTimedOut, "Connection Timed Out!");
break;
case HTTPRequestStates.TimedOut:
Debug.LogError("Processing the request Timed Out!");
error?.Invoke((int)HTTPRequestStates.TimedOut, "Processing the request Timed Out!");
break;
}
});
request.SetHeader("Authorization", "Bearer " + identityInfo.token);
request.SetHeader("Content-Type", "application/json; charset=UTF-8");
string json = JsonConvert.SerializeObject(data);
request.RawData = Encoding.UTF8.GetBytes(json);
request.Send();
}
/// <summary>
/// 修改json数据
/// </summary>
/// <typeparam name="TIn">修改数据类型</typeparam>
/// <param name="apiUrl">API URL</param>
/// <param name="data">修改数据</param>
/// <param name="success">请求成功回调</param>
/// <param name="error">请求错误回调</param>
public void PatchJson<TIn>(string apiUrl, TIn data, Action success, Action<int, string> error = null)
{
if (string.IsNullOrEmpty(apiUrl))
{
throw new ArgumentNullException($"API URL:{apiUrl}无效!");
}
HTTPRequest request = new HTTPRequest(new Uri(baseHttpUrl + apiUrl), HTTPMethods.Patch, (req, resp) =>
{
switch (req.State)
{
case HTTPRequestStates.Finished:
if (resp.IsSuccess)
{
success?.Invoke();
}
else
{
Debug.LogWarning(string.Format("Request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}",
resp.StatusCode,
resp.Message,
resp.DataAsText));
error?.Invoke(resp.StatusCode, JsonConvert.DeserializeObject<ProblemDetails>(resp.DataAsText).detail);
}
break;
case HTTPRequestStates.Error:
Debug.LogError("Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception"));
error?.Invoke((int)HTTPRequestStates.Error, "Request Finished with Error!");
break;
case HTTPRequestStates.Aborted:
Debug.LogWarning("Request Aborted!");
error?.Invoke((int)HTTPRequestStates.Aborted, "Request Aborted!");
break;
case HTTPRequestStates.ConnectionTimedOut:
Debug.LogError("Connection Timed Out!");
error?.Invoke((int)HTTPRequestStates.ConnectionTimedOut, "Connection Timed Out!");
break;
case HTTPRequestStates.TimedOut:
Debug.LogError("Processing the request Timed Out!");
error?.Invoke((int)HTTPRequestStates.TimedOut, "Processing the request Timed Out!");
break;
}
});
request.SetHeader("Authorization", "Bearer " + identityInfo.token);
request.SetHeader("Content-Type", "application/json; charset=UTF-8");
string json = JsonConvert.SerializeObject(data);
request.RawData = Encoding.UTF8.GetBytes(json);
request.Send();
}
/// <summary>
/// 删除json数据
/// </summary>
/// <param name="apiUrl">API URL</param>
/// <param name="success">请求成功回调</param>
/// <param name="error">请求错误回调</param>
public void DeleteJson(string apiUrl, Action success, Action<int, string> error = null)
{
if (string.IsNullOrEmpty(apiUrl))
{
throw new ArgumentNullException($"API URL:{apiUrl}无效!");
}
HTTPRequest request = new HTTPRequest(new Uri(baseHttpUrl + apiUrl), HTTPMethods.Delete, (req, resp) =>
{
switch (req.State)
{
case HTTPRequestStates.Finished:
if (resp.IsSuccess)
{
success?.Invoke();
}
else
{
Debug.LogWarning(string.Format("Request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}",
resp.StatusCode,
resp.Message,
resp.DataAsText));
error?.Invoke(resp.StatusCode, JsonConvert.DeserializeObject<ProblemDetails>(resp.DataAsText).detail);
}
break;
case HTTPRequestStates.Error:
Debug.LogError("Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception"));
error?.Invoke((int)HTTPRequestStates.Error, "Request Finished with Error!");
break;
case HTTPRequestStates.Aborted:
Debug.LogWarning("Request Aborted!");
error?.Invoke((int)HTTPRequestStates.Aborted, "Request Aborted!");
break;
case HTTPRequestStates.ConnectionTimedOut:
Debug.LogError("Connection Timed Out!");
error?.Invoke((int)HTTPRequestStates.ConnectionTimedOut, "Connection Timed Out!");
break;
case HTTPRequestStates.TimedOut:
Debug.LogError("Processing the request Timed Out!");
error?.Invoke((int)HTTPRequestStates.TimedOut, "Processing the request Timed Out!");
break;
}
});
request.SetHeader("Authorization", "Bearer " + identityInfo.token);
request.Send();
}
/// <summary>
/// 删除json数据
/// </summary>
/// <typeparam name="TIn">提交数据类型</typeparam>
/// <param name="apiUrl">API URL</param>
/// <param name="data">提交数据</param>
/// <param name="success">请求成功回调</param>
/// <param name="error">请求错误回调</param>
public void DeleteJson<TIn>(string apiUrl, TIn data, Action success, Action<int, string> error = null)
{
if (string.IsNullOrEmpty(apiUrl))
{
throw new ArgumentNullException($"API URL:{apiUrl}无效!");
}
HTTPRequest request = new HTTPRequest(new Uri(baseHttpUrl + apiUrl), HTTPMethods.Delete, (req, resp) =>
{
switch (req.State)
{
case HTTPRequestStates.Finished:
if (resp.IsSuccess)
{
success?.Invoke();
}
else
{
Debug.LogWarning(string.Format("Request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}",
resp.StatusCode,
resp.Message,
resp.DataAsText));
error?.Invoke(resp.StatusCode, JsonConvert.DeserializeObject<ProblemDetails>(resp.DataAsText).detail);
}
break;
case HTTPRequestStates.Error:
Debug.LogError("Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception"));
error?.Invoke((int)HTTPRequestStates.Error, "Request Finished with Error!");
break;
case HTTPRequestStates.Aborted:
Debug.LogWarning("Request Aborted!");
error?.Invoke((int)HTTPRequestStates.Aborted, "Request Aborted!");
break;
case HTTPRequestStates.ConnectionTimedOut:
Debug.LogError("Connection Timed Out!");
error?.Invoke((int)HTTPRequestStates.ConnectionTimedOut, "Connection Timed Out!");
break;
case HTTPRequestStates.TimedOut:
Debug.LogError("Processing the request Timed Out!");
error?.Invoke((int)HTTPRequestStates.TimedOut, "Processing the request Timed Out!");
break;
}
});
request.SetHeader("Authorization", "Bearer " + identityInfo.token);
request.SetHeader("Content-Type", "application/json; charset=UTF-8");
string json = JsonConvert.SerializeObject(data);
request.RawData = Encoding.UTF8.GetBytes(json);
request.Send();
}
/// <summary>
/// 文件下载:分块下载,支持下载的断点续传
/// </summary>
/// <param name="apiUrl"> API URL</param>
/// <param name="isCache">是否缓存到本地</param>
/// <param name="isFileChange">服务器文件是否变动</param>
/// <param name="complete">下载完成回调</param>
/// <param name="progress">下载进度回调</param>
/// <param name="error">下载错误回调</param>
/// <param name="localPath">本地缓存路径:包含文件名、扩展名的相对路径</param>
/// <param name="fileBytes">内存中缓存文件字节流的数组</param>
/// /// <param name="reqCallBack">传出HTTPRequest实例的回调:方便上层缓存请求来中止请求(request.Abort())</param>
public void FileDownload(string apiUrl, bool isCache, bool isFileChange, Action complete, Action<float> progress = null, Action<int, string> error = null, string localPath = null, List<byte> fileBytes = null, Action<HTTPRequest> reqCallBack = null)
{
if (string.IsNullOrEmpty(apiUrl))
throw new ArgumentNullException($"API URL:{apiUrl}无效!");
if (isCache)
{
if (string.IsNullOrEmpty(localPath))
{
throw new ArgumentNullException($"本地缓存路径:{localPath}无效!");
}
else
{
try
{
string fileName = Path.GetFileName(localPath);
if (string.IsNullOrEmpty(fileName))
{
if (fileName == null)
throw new ArgumentException($"本地缓存路径:{localPath}无效!");
if (fileName == "")
throw new ArgumentException($"本地缓存路径:{localPath}无效!");
}
}
catch (Exception e)
{
throw new ArgumentException("本地缓存路径无效!", "localPath", e);
}
}
}
else
{
if (fileBytes == null)
{
throw new ArgumentNullException("不缓存本地时,必须传入fileBytes参数!");
}
}
string tempFilePath = "";
if (isCache)
tempFilePath = $"{localPath}.temp";
long downloadFileTotalLength = -1;
long downloadStartedAt = 0;
long downloadedLength = 0;
HTTPRequest downloadRequest = new HTTPRequest(new Uri(baseHttpUrl + apiUrl), HTTPMethods.Get, (req, resp) =>
{
var fs = req.Tag as FileStream;
switch (req.State)
{
case HTTPRequestStates.Finished:
if (resp.IsSuccess)
{
if ((downloadStartedAt + downloadedLength) >= downloadFileTotalLength)
{
if (fs != null) { fs.Close(); fs.Dispose(); }
if (isCache)
{
if (File.Exists(localPath))
File.Delete(localPath);
File.Move(tempFilePath, localPath);
}
complete?.Invoke();
}
else
{
req.SetRangeHeader(downloadStartedAt + downloadedLength, downloadStartedAt + downloadedLength + fragmentSize);
req.Send();
}
}
else
{
Debug.LogWarning(string.Format("Request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}",
resp.StatusCode,
resp.Message,
resp.DataAsText));
error?.Invoke(resp.StatusCode, string.Format("Status Code: {0}-{1} Message: {2}",
resp.StatusCode,
resp.Message,
resp.DataAsText));
if (fs != null) { fs.Close(); fs.Dispose(); }
downloadRequest = null;
}
break;
case HTTPRequestStates.Error:
Debug.LogError("Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception"));
error?.Invoke((int)HTTPRequestStates.Error, "Request Finished with Error!");
if (fs != null) { fs.Close(); fs.Dispose(); }
downloadRequest = null;
break;
case HTTPRequestStates.Aborted:
Debug.LogWarning("Request Aborted!");
if (fs != null) { fs.Close(); fs.Dispose(); }
downloadRequest = null;
break;
case HTTPRequestStates.ConnectionTimedOut:
Debug.LogError("Connection Timed Out!");
error?.Invoke((int)HTTPRequestStates.ConnectionTimedOut, "Connection Timed Out!");
if (fs != null) { fs.Close(); fs.Dispose(); }
downloadRequest = null;
break;
case HTTPRequestStates.TimedOut:
Debug.LogError("Processing the request Timed Out!");
error?.Invoke((int)HTTPRequestStates.TimedOut, "Processing the request Timed Out!");
if (fs != null) { fs.Close(); fs.Dispose(); }
downloadRequest = null;
break;
default:
break;
}
downloadRequest = null;
});
reqCallBack?.Invoke(downloadRequest);
downloadRequest.OnHeadersReceived += (req, resp, headers) =>
{
var range = resp.GetRange();
if (range != null)
downloadFileTotalLength = range.ContentLength;
if (!isCache && fileBytes.Count == 0)
{
fileBytes.Capacity = (int)downloadFileTotalLength;
}
};
if (isCache)
{
if (!isFileChange)
{
if (File.Exists(tempFilePath))
{
FileInfo fileInfo = new FileInfo(tempFilePath);
downloadStartedAt = fileInfo.Length;
fileInfo = null;
}
else
{//下载了部分需要继续断点下载的情况下,下载了部分的本地临时文件被误删,则需要从新下载整个文件
downloadStartedAt = 0;
}
}
else
{
downloadStartedAt = 0;
if (File.Exists(tempFilePath))
{
File.Delete(tempFilePath);
}
}
}
else
{
downloadStartedAt = fileBytes.Count;
}
downloadRequest.OnDownloadProgress += (req, downloaded, downloadLength) =>
{
double downloadPercent = ((downloadStartedAt + downloadedLength + downloaded) / (double)downloadFileTotalLength);
progress?.Invoke((float)downloadPercent);
};
downloadRequest.OnStreamingData += (req, resp, dataFragment, dataFragmentLength) =>
{
if (isCache)
{
if (resp.IsSuccess)
{
string directory = Path.GetDirectoryName(tempFilePath);
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
var fs = req.Tag as FileStream;
if (fs == null)
req.Tag = fs = new FileStream(tempFilePath, FileMode.Append, FileAccess.Write, FileShare.Read);
fs.Write(dataFragment, 0, dataFragmentLength);
downloadedLength += dataFragmentLength;
}
}
else
{
if (resp.IsSuccess)
{
fileBytes.AddRange(dataFragment.Skip(0).Take(dataFragmentLength).ToArray());
downloadedLength += dataFragmentLength;
}
}
return true;
};
downloadRequest.SetHeader("Authorization", "Bearer " + identityInfo.token);
downloadRequest.SetRangeHeader(downloadStartedAt, downloadStartedAt + fragmentSize);
downloadRequest.DisableCache = true;
downloadRequest.StreamFragmentSize = fragmentSize;
downloadRequest.Send();
}
/// <summary>
/// 文件上传:分块上传,支持上传的断点续传
/// </summary>
/// <param name="apiUrl">API URL</param>
/// <param name="objectName">服务端桶下的路径</param>
/// <param name="filePath">上传文件在本地的路径</param>
/// <param name="complete">上传完成回调</param>
/// <param name="progress">上传进度回调</param>
/// <param name="error">上传错误回调</param>
/// <param name="reqCallBack">传出HTTPRequest实例的回调:方便上层缓存请求来中止请求(request.Abort())</param>
public async UniTaskVoid FileUploadAsync(string apiUrl, string objectName, string filePath, Action complete, Action<float> progress = null, Action<int, string> error = null, Action<HTTPRequest> reqCallBack = null)
{
if (string.IsNullOrEmpty(apiUrl))
throw new ArgumentNullException($"API URL:{apiUrl}无效!");
if (string.IsNullOrEmpty(objectName))
{
throw new ArgumentNullException($"文件路径:{objectName}无效!");
}
else
{
try
{
string fileName = Path.GetFileName(objectName);
if (string.IsNullOrEmpty(fileName))
{
if (fileName == null)
throw new ArgumentException($"本地缓存路径:{objectName}无效!");
if (fileName == "")
throw new ArgumentException($"本地缓存路径:{objectName}无效!");
}
}
catch (Exception e)
{
throw new ArgumentException("本地缓存路径无效!", "objectName", e);
}
}
if (string.IsNullOrEmpty(filePath))
{
throw new ArgumentNullException($"文件路径:{filePath}无效!");
}
else
{
try
{
string fileName = Path.GetFileName(filePath);
if (string.IsNullOrEmpty(fileName))
{
if (fileName == null)
throw new ArgumentException($"本地缓存路径:{filePath}无效!");
if (fileName == "")
throw new ArgumentException($"本地缓存路径:{filePath}无效!");
}
}
catch (Exception e)
{
throw new ArgumentException("本地缓存路径无效!", "filePath", e);
}
}
string md5;
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
md5 = HashAlgorithm<MD5>.ComputeHash(fs);
}
string uploadFileName = Path.GetFileName(objectName);
var savedMultipartUploadInfos = GetMultipartUploadInfos(objectName);
NewMultipartUploadResult newMultipartUploadResult = new NewMultipartUploadResult();
MultipartUploadInfos multipartUploadInfos = new MultipartUploadInfos();
if (objectName == savedMultipartUploadInfos.newMultipartUploadResult.objectName && md5 == savedMultipartUploadInfos.md5)
{
newMultipartUploadResult = savedMultipartUploadInfos.newMultipartUploadResult;
}
else
{
string directoryName = Path.GetDirectoryName(objectName);
string newMultipartUploadUrl = $"{newMultipartUploadApi}/{directoryName}?keepOriginalName={true}&fileName={uploadFileName}";
//初始化分块上传事件
newMultipartUploadResult = await NewMultipartUpload(newMultipartUploadUrl, error);
multipartUploadInfos.newMultipartUploadResult = newMultipartUploadResult;
multipartUploadInfos.md5 = md5;
SaveMultipartUploadInfos(objectName, $"{JsonConvert.SerializeObject(multipartUploadInfos)}");
savedMultipartUploadInfos = GetMultipartUploadInfos(objectName);
}
//分块上传文件处理
using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
string multipartUploadUrl;
List<PartInfo> partInfos = new List<PartInfo>();
double uploadPercent;
//上传文件总长度
long uploadFileTotalSize = fileStream.Length;
//上传文件剩余总长度
long uploadFileRemainTotalSize = fileStream.Length;
//分块总数
int fragmentTotalCount = (int)Math.Ceiling((double)uploadFileTotalSize / fragmentSize);
//当前上传分块数
int currentUploadFragmentCount = 0;
//上传开始位置
long uploadStartAt = 0;
//缓存分块读取的字节流数组
byte[] bytes = new byte[fragmentSize];
//实际读取长度
int bytesLength = 0;
long uploadProcessedBytes = savedMultipartUploadInfos.uploadProcessedBytes;
if (uploadProcessedBytes > 0)
{
uploadFileRemainTotalSize = uploadFileTotalSize - uploadProcessedBytes;
currentUploadFragmentCount = (int)Math.Ceiling((double)uploadProcessedBytes / fragmentSize);
uploadStartAt = uploadProcessedBytes;
fileStream.Position = uploadStartAt;
partInfos = savedMultipartUploadInfos.partInfos;
}
else
DeleteKeys(objectName);
while (currentUploadFragmentCount < fragmentTotalCount)
{
if (uploadFileRemainTotalSize < fragmentSize)
{
bytes = new byte[uploadFileRemainTotalSize];
bytesLength = fileStream.Read(bytes, 0, Convert.ToInt32(uploadFileRemainTotalSize));
}
else
{
bytes = new byte[fragmentSize];
bytesLength = fileStream.Read(bytes, 0, fragmentSize);
}
multipartUploadUrl = $"{apiUrl}?uploadId={newMultipartUploadResult.uploadId}&partNumber={currentUploadFragmentCount + 1}";
//上传文件分块数据
PartInfo partInfo = await MultipartUpload(multipartUploadUrl, bytes, uploadFileName, error, reqCallBack);
partInfos.Add(partInfo);
uploadFileRemainTotalSize -= bytesLength;
currentUploadFragmentCount++;
uploadStartAt += bytesLength;
fileStream.Position = uploadStartAt;
multipartUploadInfos.uploadProcessedBytes = uploadStartAt;
multipartUploadInfos.partInfos = partInfos;
SaveMultipartUploadInfos(objectName, $"{JsonConvert.SerializeObject(multipartUploadInfos)}");
uploadPercent = uploadStartAt / (double)fileStream.Length;
progress?.Invoke((float)uploadPercent);
}
//分块上传完成处理
if (currentUploadFragmentCount == fragmentTotalCount)
{
string completeMultipartUploadUrl = $"{completeMultipartUploadApi}/{objectName}?uploadId={newMultipartUploadResult.uploadId}";
await CompleteMultipartUpload(completeMultipartUploadUrl, partInfos, complete, error);
DeleteKeys(objectName);
}
}
}
/// <summary>
/// 初始化分块上传事件
/// </summary>
/// <param name="url"></param>
/// <param name="error"></param>
/// <returns></returns>
private async UniTask<NewMultipartUploadResult> NewMultipartUpload(string url, Action<int, string> error)
{
HTTPRequest req = new HTTPRequest(new Uri(baseHttpUrl + url), HTTPMethods.Post);
req.SetHeader("Authorization", "Bearer " + identityInfo.token);
try
{
var json = await req.GetAsStringAsync();
NewMultipartUploadResult newMultipartUploadResult = JsonConvert.DeserializeObject<NewMultipartUploadResult>(json);
return newMultipartUploadResult;
}
catch (AsyncHTTPException ex)
{
if (ex.StatusCode != 0)
{
error?.Invoke(ex.StatusCode, string.Format("Status Code: {0}-{1} Message: {2}",
ex.StatusCode,
ex.Message,
ex.Content));
}
else
{
error?.Invoke((int)req.State, ex.Message);
}
throw ex;
}
}
/// <summary>
/// 上传文件分块数据
/// </summary>
/// <param name="url"></param>
/// <param name="bytes"></param>
/// <param name="error"></param>
/// <param name="reqCallBack"></param>
/// <returns></returns>
private async UniTask<PartInfo> MultipartUpload(string url, byte[] bytes, string fileName, Action<int, string> error, Action<HTTPRequest> reqCallBack = null)
{
HTTPRequest req = new HTTPRequest(new Uri(baseHttpUrl + url), HTTPMethods.Post);
req.SetHeader("Authorization", "Bearer " + identityInfo.token);
req.AddBinaryData("file", bytes, fileName, "application/octet-stream");
reqCallBack?.Invoke(req);
try
{
var json = await req.GetAsStringAsync();
PartInfo partInfo = JsonConvert.DeserializeObject<PartInfo>(json);
return partInfo;
}
catch (AsyncHTTPException ex)
{
if (ex.StatusCode != 0)
{
error?.Invoke(ex.StatusCode, string.Format("Status Code: {0}-{1} Message: {2}",
ex.StatusCode,
ex.Message,
ex.Content));
}
else
{
error?.Invoke((int)req.State, ex.Message);
}
throw ex;
}
}
/// <summary>
/// 完成分块上传事件
/// </summary>
/// <param name="url"></param>
/// <param name="partInfos"></param>
/// <param name="complete"></param>
/// <param name="error"></param>
/// <returns></returns>
private async UniTask CompleteMultipartUpload(string url, List<PartInfo> partInfos, Action complete, Action<int, string> error)
{
HTTPRequest req = new HTTPRequest(new Uri(baseHttpUrl + url), HTTPMethods.Post);
req.SetHeader("Authorization", "Bearer " + identityInfo.token);
req.SetHeader("Content-Type", "application/json; charset=UTF-8");
List<PartNumberETag> partNumberETags = new List<PartNumberETag>();
foreach (var partInfo in partInfos)
{
partNumberETags.Add(new PartNumberETag() { partNumber = partInfo.partNumber, eTag = partInfo.eTag });
}
string jsonData = JsonConvert.SerializeObject(partNumberETags);
req.RawData = Encoding.UTF8.GetBytes(jsonData);
try
{
await req.GetAsStringAsync();
complete?.Invoke();
}
catch (AsyncHTTPException ex)
{
if (ex.StatusCode != 0)
{
error?.Invoke(ex.StatusCode, string.Format("Status Code: {0}-{1} Message: {2}",
ex.StatusCode,
ex.Message,
ex.Content));
}
else
{
error?.Invoke((int)req.State, ex.Message);
}
throw ex;
}
}
/// <summary>
/// 保存文件分块上传信息
/// </summary>
/// <param name="objectName"></param>
/// <param name="str"></param>
private void SaveMultipartUploadInfos(string objectName, string str)
{
PlayerPrefs.SetString(objectName + multipartUploadInfosKey, str);
}
/// <summary>
/// 获取保存的文件分块上传信息
/// </summary>
/// <param name="objectName"></param>
/// <returns></returns>
private MultipartUploadInfos GetMultipartUploadInfos(string objectName)
{
string str = PlayerPrefs.GetString(objectName + multipartUploadInfosKey, "");
if (!string.IsNullOrEmpty(str))
{
var multipartUploadInfos = JsonConvert.DeserializeObject<MultipartUploadInfos>(str);
return multipartUploadInfos;
}
else
{
return new MultipartUploadInfos();
}
}
/// <summary>
/// 删除保存的文件分块上传信息
/// </summary>
/// <param name="objectName"></param>
private void DeleteKeys(string objectName)
{
PlayerPrefs.DeleteKey(objectName + multipartUploadInfosKey);
PlayerPrefs.Save();
}
/// <summary>
/// 文件夹复制
/// </summary>
/// <param name="apiUrl"></param>
/// <param name="complete"></param>
/// <param name="error"></param>
public void FileDirectoryCopy(string apiUrl, Action<ListObjectsResult> complete, Action<int, string> error = null)
{
if (string.IsNullOrEmpty(apiUrl))
throw new ArgumentNullException($"API URL:{apiUrl}无效!");
HTTPRequest directoryCopyRequest = new HTTPRequest(new Uri(baseHttpUrl + apiUrl), HTTPMethods.Put, (req, resp) =>
{
switch (req.State)
{
case HTTPRequestStates.Finished:
if (resp.IsSuccess)
{
ListObjectsResult result = JsonConvert.DeserializeObject<ListObjectsResult>(resp.DataAsText);
complete?.Invoke(result);
}
else
{
Debug.LogWarning(string.Format("Request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}",
resp.StatusCode,
resp.Message,
resp.DataAsText));
error?.Invoke(resp.StatusCode, JsonConvert.DeserializeObject<ProblemDetails>(resp.DataAsText).detail);
}
break;
case HTTPRequestStates.Error:
Debug.LogError("Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception"));
error?.Invoke((int)HTTPRequestStates.Error, "Request Finished with Error!");
break;
case HTTPRequestStates.Aborted:
Debug.LogWarning("Request Aborted!");
break;
case HTTPRequestStates.ConnectionTimedOut:
Debug.LogError("Connection Timed Out!");
error?.Invoke((int)HTTPRequestStates.ConnectionTimedOut, "Connection Timed Out!");
break;
case HTTPRequestStates.TimedOut:
Debug.LogError("Processing the request Timed Out!");
error?.Invoke((int)HTTPRequestStates.TimedOut, "Processing the request Timed Out!");
break;
}
});
directoryCopyRequest.SetHeader("Authorization", "Bearer " + identityInfo.token);
directoryCopyRequest.Send();
}
public string GetBaseHttpUrl()
{
return baseHttpUrl;
}
#region 刷新令牌
/// <summary>
/// 启动刷新令牌计时器
/// </summary>
public void StartTimer()
{
RefreshTokenInfo refreshTokenInfo = new RefreshTokenInfo();
refreshTokenInfo.Token = identityInfo.token;
refreshTokenInfo.RefreshToken = identityInfo.refreshToken;
StartCoroutine(Timer(refreshTokenInfo));
}
/// <summary>
/// 计时器:根据登录成功后,服务端返回的身份信息中的token过期时间(分钟),定时提前1分钟来刷新token
/// </summary>
/// <param name="refreshTokenInfo">刷新令牌信息</param>
/// <returns></returns>
private IEnumerator Timer(RefreshTokenInfo refreshTokenInfo)
{
while (true)
{
yield return new WaitForSeconds((float)(identityInfo.expires - 1) * 60);
PostJson<RefreshTokenInfo, IdentityInfo>(
refreshTokenApi, refreshTokenInfo,
RefreshTokenSuccessed,
RefreshTokenError
);
}
}
private void RefreshTokenSuccessed(IdentityInfo identityInfo)
{
this.identityInfo = identityInfo;
//如果处于管理员界面处理页面token刷新
//AdminManager.Instance.RefreshWebToke();
}
private void RefreshTokenError(int responseCode, string errorMsg)
{
Debug.LogError($"刷新身份错误:{responseCode},{errorMsg}");
}
#endregion
}