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;
///
/// 服务器配置文件对应类型
///
public class ServerConfig
{
public string Ip;
public string Port;
}
///
/// 刷新令牌信息。
///
public class RefreshTokenInfo
{
///
/// JWT令牌。
///
public string Token { get; set; }
///
/// 刷新令牌。
///
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(s);
baseHttpUrl = "http://" + config.Ip + ":" + config.Port;
MessageDispatcher.SendMessage("ServerConfigLoaded");
});
}
}
///
/// 获取json数据
///
/// 返回数据类型
/// API URL
/// 请求成功回调
/// 请求错误回调
public void GetJson(string apiUrl, Action success, Action 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(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(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();
}
///
/// 提交json数据
///
/// 提交数据类型
/// 返回数据类型
/// API URL
/// 提交数据
/// 请求成功回调
/// 请求错误回调
public void PostJson(string apiUrl, TIn data, Action success, Action 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(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(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();
}
///
/// 提交json数据
///
/// 提交数据类型
/// API URL
/// 提交数据
/// 请求成功回调
/// 请求错误回调
public void PostJson(string apiUrl, TIn data, Action success, Action 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(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();
}
///
/// 提交json数据
///
/// 返回数据类型
/// API URL
/// 请求成功回调
/// 请求错误回调
public void PostJson(string apiUrl, Action success, Action 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(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(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();
}
///
/// 提交json数据
///
/// API URL
/// 请求成功回调
/// 请求错误回调
public void PostJson(string apiUrl, Action success, Action 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(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();
}
///
/// 修改json数据
///
/// 修改数据类型
/// 返回数据类型
/// API URL
/// 修改数据
/// 请求成功回调
/// 请求错误回调
public void PatchJson(string apiUrl, TIn data, Action success, Action 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(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(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();
}
///
/// 修改json数据
///
/// 修改数据类型
/// API URL
/// 修改数据
/// 请求成功回调
/// 请求错误回调
public void PatchJson(string apiUrl, TIn data, Action success, Action 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(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();
}
///
/// 删除json数据
///
/// API URL
/// 请求成功回调
/// 请求错误回调
public void DeleteJson(string apiUrl, Action success, Action 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(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();
}
///
/// 删除json数据
///
/// 提交数据类型
/// API URL
/// 提交数据
/// 请求成功回调
/// 请求错误回调
public void DeleteJson(string apiUrl, TIn data, Action success, Action 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(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();
}
///
/// 文件下载:分块下载,支持下载的断点续传
///
/// API URL
/// 是否缓存到本地
/// 服务器文件是否变动
/// 下载完成回调
/// 下载进度回调
/// 下载错误回调
/// 本地缓存路径:包含文件名、扩展名的相对路径
/// 内存中缓存文件字节流的数组
/// /// 传出HTTPRequest实例的回调:方便上层缓存请求来中止请求(request.Abort())
public void FileDownload(string apiUrl, bool isCache, bool isFileChange, Action complete, Action progress = null, Action error = null, string localPath = null, List fileBytes = null, Action 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();
}
///
/// 文件上传:分块上传,支持上传的断点续传
///
/// API URL
/// 服务端桶下的路径
/// 上传文件在本地的路径
/// 上传完成回调
/// 上传进度回调
/// 上传错误回调
/// 传出HTTPRequest实例的回调:方便上层缓存请求来中止请求(request.Abort())
public async UniTaskVoid FileUploadAsync(string apiUrl, string objectName, string filePath, Action complete, Action progress = null, Action error = null, Action 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.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 partInfos = new List();
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);
}
}
}
///
/// 初始化分块上传事件
///
///
///
///
private async UniTask NewMultipartUpload(string url, Action 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(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;
}
}
///
/// 上传文件分块数据
///
///
///
///
///
///
private async UniTask MultipartUpload(string url, byte[] bytes, string fileName, Action error, Action 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(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;
}
}
///
/// 完成分块上传事件
///
///
///
///
///
///
private async UniTask CompleteMultipartUpload(string url, List partInfos, Action complete, Action 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 partNumberETags = new List();
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;
}
}
///
/// 保存文件分块上传信息
///
///
///
private void SaveMultipartUploadInfos(string objectName, string str)
{
PlayerPrefs.SetString(objectName + multipartUploadInfosKey, str);
}
///
/// 获取保存的文件分块上传信息
///
///
///
private MultipartUploadInfos GetMultipartUploadInfos(string objectName)
{
string str = PlayerPrefs.GetString(objectName + multipartUploadInfosKey, "");
if (!string.IsNullOrEmpty(str))
{
var multipartUploadInfos = JsonConvert.DeserializeObject(str);
return multipartUploadInfos;
}
else
{
return new MultipartUploadInfos();
}
}
///
/// 删除保存的文件分块上传信息
///
///
private void DeleteKeys(string objectName)
{
PlayerPrefs.DeleteKey(objectName + multipartUploadInfosKey);
PlayerPrefs.Save();
}
///
/// 文件夹复制
///
///
///
///
public void FileDirectoryCopy(string apiUrl, Action complete, Action 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(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(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 刷新令牌
///
/// 启动刷新令牌计时器
///
public void StartTimer()
{
RefreshTokenInfo refreshTokenInfo = new RefreshTokenInfo();
refreshTokenInfo.Token = identityInfo.token;
refreshTokenInfo.RefreshToken = identityInfo.refreshToken;
StartCoroutine(Timer(refreshTokenInfo));
}
///
/// 计时器:根据登录成功后,服务端返回的身份信息中的token过期时间(分钟),定时提前1分钟来刷新token
///
/// 刷新令牌信息
///
private IEnumerator Timer(RefreshTokenInfo refreshTokenInfo)
{
while (true)
{
yield return new WaitForSeconds((float)(identityInfo.expires - 1) * 60);
PostJson(
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
}