示例一:上传视频
/// <summary> /// 上传视频 /// </summary> /// <param name="url">上传视频地址</param> /// <param name="bytes">视频文件内容</param> /// <returns></returns> IEnumerator UpLoadVideo(string url, byte[] bytes) { yield return null; WWWForm form = new WWWForm(); form.AddField("id", 123); form.AddBinaryData("file", bytes, "my_video.mp4", "video/mp4"); //byte[] bodyRaw = Encoding.UTF8.GetBytes(postDataString);//发送原始数据(即,数据不组织成表单) //UnityWebRequest request = new UnityWebRequest(url, "POST"); UnityWebRequest request = UnityWebRequest.Post(url, form); request.timeout = 5; //https://docs.unity3d.com/ScriptReference/Networking.UnityWebRequest-useHttpContinue.html //true: 包含Expect: 100-Continue请求头, http会提前返回,这会导至后面的isNetworkError=true, responseCode=100 //false: 确保http请求完毕后再返回 request.useHttpContinue = false; request.chunkedTransfer = false; //false:不希望服务器分块返回数据 request.SetRequestHeader("Content-Type", "application/json;charset=utf-8"); //如果不指定post表单,需要这样上传原始数据 //request.uploadHandler = (UploadHandler)new UploadHandlerRaw(bodyRaw); //request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(); //加上yield return 直到请求完成程序才会继续往下执行 //yield return request.SendWebRequest(); //调用后会立即返回,程序继续往下执行 request.SendWebRequest(); while (!request.isDone) { Debug.Log("上传进度: " + request.uploadProgress); yield return new WaitForSeconds(0.1f); } if (request.isHttpError || request.isNetworkError || request.responseCode != 200) { Debug.LogErrorFormat("上传视频出错\n{0}", request.error); yield break; //跳出协程 } Debug.Log("上传视频成功!"); } // Post原始数据(即,非表单) public IEnumerator PostRaw(string url, string data, ActioncompletedCallback = null, Action errorCallback = null) { yield return null; byte[] postBytes = Encoding.UTF8.GetBytes(data); DownloadHandler downloadHandler = new DownloadHandlerBuffer(); UploadHandler uploadHandler = new UploadHandlerRaw(postBytes); UnityWebRequest request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST); request.SetRequestHeader("Content-Type", "application/json;charset=utf-8"); request.SetRequestHeader("Accept", "application/json"); //设置Accept-Charset会报InvalidOperationException异常 //InvalidOperationException: Cannot override system-specified headers //request.SetRequestHeader("Accept-Charset", "utf-8"); //request.SetRequestHeader("Content-Length", postBytes.Length.ToString()); request.timeout = 30; //单位:秒 request.useHttpContinue = false;//false:直接上传post数据,不需要发送前征询服务器情况 request.chunkedTransfer = false;//false:不希望服务器分块返回数据 request.downloadHandler = downloadHandler; //如果不指定post表单,需要这样上传原始数据 request.uploadHandler = uploadHandler; //yield return request.SendWebRequest(); //同步请求(请求完成后才会继续往下执行) request.SendWebRequest(); //异步请求 while (!request.isDone) yield return new WaitForSeconds(0.1f); if (request.isHttpError || request.isNetworkError || request.responseCode != 200) { Debug.LogErrorFormat("Post Error\n{0}", request.error); if (errorCallback != null) errorCallback(); yield break;//跳出协程 } Debug.Log("Post完成!"); Debug.LogFormat("result={0}", request.downloadHandler.text); string json = request.downloadHandler.text; if (completedCallback != null) completedCallback(json); }
示例二:上传器
using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Net.Mime; using System.Text; using UnityEngine; using UnityEngine.Networking; /// <summary> /// 资源上传器 (multipart/form-data) /// </summary> public class AssetUpLoader : MonoBehaviour { public static AssetUpLoader Instance { get; private set; } public bool dontDestroyOnLoad = true; public bool isSingleInstance = true; //上传队列 private Queue<AssetObject> m_UploadQueue = new Queue<AssetObject>(); private bool m_UpLoading = false; private void Awake() { if (dontDestroyOnLoad) DontDestroyOnLoad(gameObject); if (isSingleInstance) Instance = this; } private void Update() { if (m_UpLoading || m_UploadQueue.Count <= 0) return; AssetObject asset = m_UploadQueue.Dequeue(); StartCoroutine(CoroutineUpLoad(asset)); } public void UpLoad(AssetObject asset) { if (asset == null || string.IsNullOrEmpty(asset.url)) return; m_UploadQueue.Enqueue(asset); } // 上传本地资源 public void UpLoadLocal(AssetObject asset) { StartCoroutine(UpLoadLocalCoroutine(asset)); } private IEnumerator UpLoadLocalCoroutine(AssetObject asset) { yield return null; Texture2D texture = null; using (WWW www = new WWW(asset.localPath)) { while (!www.isDone) yield return null; if (!string.IsNullOrEmpty(www.error)) { Debug.LogErrorFormat("load local asset error: {0}\n{1}", www.error, www.url); yield break; } texture = www.texture; } if (texture != null) { asset.bytes = texture.EncodeToJPG(); m_UploadQueue.Enqueue(asset); } } /// <summary> /// 上传资源 /// </summary> /// <param name="url">上传地址</param> /// <param name="bytes">资源内容</param> /// <returns></returns> IEnumerator CoroutineUpLoad(AssetObject assetObject) { Debug.LogFormat("begin upload: {0}", assetObject.fileName); m_UpLoading = true; yield return null; DownloadHandler downloadHandler = new DownloadHandlerBuffer(); UnityWebRequest request = assetObject.unityWebRequest; request.downloadHandler = downloadHandler; request.SendWebRequest(); while (!request.isDone) { Debug.Log("上传进度: " + request.uploadProgress); assetObject.uploadProgress = request.uploadProgress; if (assetObject.OnProgressCallback != null) assetObject.OnProgressCallback(assetObject); yield return new WaitForSeconds(0.1f); } if (request.isHttpError || request.isNetworkError || request.responseCode != 200) { Debug.LogErrorFormat("上传资源出错\n{0}\n{1}", request.error, assetObject.url); if (assetObject.OnErrorCallback != null) assetObject.OnErrorCallback(assetObject); assetObject.Dispose(); m_UpLoading = false; yield break; } Debug.Log("上传资源完成!"); Debug.LogFormat("{0}", request.downloadHandler.text); assetObject.result = request.downloadHandler.text; if (assetObject.OnCompletedCallback != null) assetObject.OnCompletedCallback(assetObject); assetObject.Dispose(); m_UpLoading = false; } /// <summary> /// 表单编码方式 /// Multipart格式文档 // https://www.cnblogs.com/greenerycn/archive/2010/05/15/csharp_http_post.html // https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4 /// </summary> public class FormEnctype { //对于发送大量二进制数据或包含非ASCII字符的文本效率不高 public const string APPLICATION_X_WWW_FORM_URLENCODED = "application/x-www-form-urlencoded"; //提交包含文件,非ASCII数据和二进制数据的表单 public const string MULTIPART_FORM_DATA = "multipart/form-data"; } /// <summary> /// 资源对象 /// </summary> public class AssetObject { public string headerContentType = FormEnctype.APPLICATION_X_WWW_FORM_URLENCODED; public string id = string.Empty; //用户ID public string url = string.Empty; public string localPath = string.Empty; //本地资源路径 public byte[] bytes = null; //.jpg "image/jpeg" //.mp4 "video/mp4" public string mimeType = string.Empty; public string fileName = string.Empty; public float uploadProgress = 0.0f; public string result = string.Empty; //服务器返回数据 public Action<AssetObject> OnCompletedCallback; public Action<AssetObject> OnProgressCallback; public Action<AssetObject> OnErrorCallback; public string MimeType { get { if (string.IsNullOrWhiteSpace(mimeType) && !string.IsNullOrWhiteSpace(fileName)) { if (fileName.EndsWith(".jpg")) mimeType = "image/jpeg"; else if (fileName.EndsWith(".png")) mimeType = "image/png"; else if (fileName.EndsWith(".mp4")) mimeType = "video/mp4"; } return mimeType; } } public WWWForm wwwForm { get { WWWForm form = new WWWForm(); form.AddField("id", id); form.AddBinaryData("file", bytes, fileName, MimeType); return form; } } public UnityWebRequest unityWebRequest { get { UnityWebRequest request = null; switch (headerContentType) { case FormEnctype.APPLICATION_X_WWW_FORM_URLENCODED: request = UnityWebRequest.Post(url, wwwForm); request.SetRequestHeader("Content-Type", "application/json;charset=utf-8"); break; case FormEnctype.MULTIPART_FORM_DATA: //分界线 byte[] boundary = UnityWebRequest.GenerateBoundary(); //结束分界线 byte[] terminate = Encoding.UTF8.GetBytes(String.Concat("\r\n--", Encoding.UTF8.GetString(boundary), "--")); //Content-Type string contentType = String.Concat("multipart/form-data; boundary=", Encoding.UTF8.GetString(boundary)); //表单 List<IMultipartFormSection> form = new List<IMultipartFormSection>(); form.Add(new MultipartFormFileSection("file", bytes, Path.GetFileName(fileName), MimeType)); //序列化表单 byte[] formSections = UnityWebRequest.SerializeFormSections(form, boundary); byte[] body = new byte[formSections.Length + terminate.Length]; Buffer.BlockCopy(formSections, 0, body, 0, formSections.Length); Buffer.BlockCopy(terminate, 0, body, formSections.Length, terminate.Length); request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST); request.uploadHandler = (UploadHandler)new UploadHandlerRaw(body); request.uploadHandler.contentType = contentType; break; } request.timeout = 30; request.useHttpContinue = false; request.chunkedTransfer = false; return request; } } public void Dispose() { bytes = null; OnCompletedCallback = null; OnProgressCallback = null; OnErrorCallback = null; } } }
Http请求
using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; using UnityEngine; using UnityEngine.Networking; /// <summary> /// 与Web端交互 /// </summary> public class HttpWebRequest : MonoBehaviour { public static HttpWebRequest Instance { get; private set; } private void Awake() { DontDestroyOnLoad(gameObject); Instance = this; } // Delete请求 public void Delete(string url, Action<string> completedCallback = null, Action<string> errorCallback = null) { StartCoroutine(DeleteRequest(url, completedCallback, errorCallback)); } public IEnumerator DeleteRequest(string url, Action<string> completedCallback = null, Action<string> errorCallback = null) { yield return null; Debug.LogFormat("DELETE: {0}", url); UnityWebRequest request = DeleteUnityWebRequest(url); //yield return request.SendWebRequest(); //同步请求(请求完成后才会继续往下执行) request.SendWebRequest(); //异步请求 while (!request.isDone) yield return new WaitForSeconds(0.1f); if (request.isHttpError || request.isNetworkError || request.responseCode != 200) { Debug.LogErrorFormat("DELETE Error\n{0}", request.error); if (errorCallback != null) errorCallback(request.error); yield break;//跳出协程 } Debug.Log("DELETE 请求返回!"); Debug.LogFormat("result={0}", request.downloadHandler.text); string json = request.downloadHandler.text; if (completedCallback != null) completedCallback(json); } // Get请求 public void Get(string url, Action<string> completedCallback = null, Action<string> errorCallback = null) { StartCoroutine(GetRequest(url, completedCallback, errorCallback)); } public IEnumerator GetRequest(string url, Action<string> completedCallback = null, Action<string> errorCallback = null) { yield return null; Debug.LogFormat("GET: {0}", url); UnityWebRequest request = GetUnityWebRequest(url); //yield return request.SendWebRequest(); //同步请求(请求完成后才会继续往下执行) request.SendWebRequest(); //异步请求 while (!request.isDone) yield return new WaitForSeconds(0.1f); if (request.isHttpError || request.isNetworkError || request.responseCode != 200) { Debug.LogErrorFormat("GET Error\n{0}", request.error); if (errorCallback != null) errorCallback(request.error); yield break;//跳出协程 } Debug.Log("GET 请求返回!"); Debug.LogFormat("result={0}", request.downloadHandler.text); string json = request.downloadHandler.text; if (completedCallback != null) completedCallback(json); } // Post原始数据(即,非表单) public void Post(string url, string data, Action<string> completedCallback = null, Action<string> errorCallback = null) { StartCoroutine(PostRaw(url, data, completedCallback, errorCallback)); } // Post原始数据(即,非表单) public IEnumerator PostRaw(string url, string data, Action<string> completedCallback = null, Action<string> errorCallback = null) { yield return null; Debug.LogFormat("POST: {0}\n{1}", url, data); byte[] postBytes = Encoding.UTF8.GetBytes(data); UnityWebRequest request = PostUnityWebRequest(url, data); //yield return request.SendWebRequest(); //同步请求(请求完成后才会继续往下执行) request.SendWebRequest(); //异步请求 while (!request.isDone) yield return new WaitForSeconds(0.1f); if (request.isHttpError || request.isNetworkError || request.responseCode != 200) { Debug.LogErrorFormat("POST Error\n{0}", request.error); if (errorCallback != null) errorCallback(request.error); yield break;//跳出协程 } Debug.Log("POST 请求返回!"); Debug.LogFormat("result={0}", request.downloadHandler.text); string json = request.downloadHandler.text; if (completedCallback != null) completedCallback(json); } // GET UnityWebRequest public UnityWebRequest GetUnityWebRequest(string url) { DownloadHandler downloadHandler = new DownloadHandlerBuffer(); UnityWebRequest request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbGET); request.SetRequestHeader("Content-Type", "application/json"); request.SetRequestHeader("Accept", "application/json"); request.timeout = 10; request.useHttpContinue = false; request.chunkedTransfer = false; request.downloadHandler = downloadHandler; return request; } // POST UnityWebRequest public UnityWebRequest PostUnityWebRequest(string url, string postData) { byte[] postBytes = Encoding.UTF8.GetBytes(postData); DownloadHandler downloadHandler = new DownloadHandlerBuffer(); UnityWebRequest request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST); request.SetRequestHeader("Content-Type", "application/json"); request.SetRequestHeader("Accept", "application/json"); request.timeout = 10; request.useHttpContinue = false; request.chunkedTransfer = false; request.downloadHandler = downloadHandler; request.uploadHandler = (UploadHandler)new UploadHandlerRaw(postBytes); return request; } // DELETE UnityWebRequest public UnityWebRequest DeleteUnityWebRequest(string url) { DownloadHandler downloadHandler = new DownloadHandlerBuffer(); UnityWebRequest request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbDELETE); request.SetRequestHeader("Content-Type", "application/json"); request.SetRequestHeader("Accept", "application/json"); request.timeout = 10; request.useHttpContinue = false; request.chunkedTransfer = false; request.downloadHandler = downloadHandler; return request; } }
UnityWebRequest request = new UnityWebRequest();
// Https 需要设置证书验证
request.certificateHandler = new AcceptAllCertificatesSignedWithASpecificKeyPublicKey();
using UnityEngine; using UnityEngine.Networking; using System.Security.Cryptography.X509Certificates; /// <summary> /// https://docs.unity3d.com/ScriptReference/Networking.CertificateHandler.ValidateCertificate.html /// https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#.Net /// </summary> public class AcceptAllCertificatesSignedWithASpecificKeyPublicKey : CertificateHandler { // Encoded RSAPublicKey private static string PUB_KEY = "3082010A0282010100C3D57BDD8594A0E4948CF35B24B2A5CC3D7455E8DADAF9C410EE6CCD7DDD0CEB4A9A62D4E065AED004E0467107A93CAF69A7CAB61A57A306171213F39C3E601D621B144D8C97674A9753177713C3A9571D1821D8EAAF821E21F94F88658E2CB68EC7994B5130CD24C89813BDD19F7DDA331900D5C70310F65FC5624BC65EF6327F1EF74896B18A59FD34F095762F9B376D5F74D187ECA86D807CACF37A597E89B2A0E690C4146971FBEA641E61AFA28C0F58A453092DEA570BA405C720A77621711BCE1A902CEC13BCC34FAEFBD67E7C671F3A85883D2F6D583843F000B1FCCAA068C09FA5DEF69F800002D332A60549C7216B3FF929B2B562EF0E3E97B776E90203010001"; //负责拒绝或接受在 https 请求上接收到的证书 //注意:自定义证书验证目前仅针对以下平台实施 - Android、iOS、tvOS 和桌面平台 //如果不需要验证,可以让这个方法直接返回true protected override bool ValidateCertificate(byte[] certificateData) { X509Certificate2 certificate = new X509Certificate2(certificateData); string pk = certificate.GetPublicKeyString(); if (pk.Equals(PUB_KEY)) return true; // Bad dog return false; } }
private IEnumerator PostFormCoroutine(string url, WWWForm form)
UnityWebRequest request = UnityWebRequest.Post(url, form);
request.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded");
request.timeout = 5;
yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.ProtocolError ||
request.result == UnityWebRequest.Result.ConnectionError ||
request.result == UnityWebRequest.Result.DataProcessingError ||
request.responseCode != 200)
{
Debug.LogErrorFormat("Http请求出错({0})\n{1}", request.responseCode, request.error);
yield break; //跳出协程
}
string text = request.downloadHandler.text;
Debug.Log(text);
}
private IEnumerator PostJsonCoroutine(string url, string json, string token)
UnityWebRequest request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST);
byte[] data = Encoding.UTF8.GetBytes(json);
request.uploadHandler = (UploadHandler)new UploadHandlerRaw(data);
request.downloadHandler = new DownloadHandlerBuffer();
request.SetRequestHeader("Content-Type", "application/json;charset=utf-8");
//认证信息根据项目实际情况传参,这里仅作示范
request.SetRequestHeader("Authorization", token);
request.timeout = 5;
yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.ProtocolError ||
request.result == UnityWebRequest.Result.ConnectionError ||
request.result == UnityWebRequest.Result.DataProcessingError ||
request.responseCode != 200)
{
Debug.LogErrorFormat("Http请求出错({0})\n{1}", request.responseCode, request.error);
yield break; //跳出协程
}
string text = request.downloadHandler.text;
Debug.Log(text);
}
注意,UnityWebRequest.Post()方法有bug,会使传到后台的json数据多个无效字符,导致json解析报错。
using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; using Unity.VisualScripting; using UnityEngine; using UnityEngine.Networking; /// <summary> /// Web请求辅助类 /// Unity 2022+ 默认不支持Http请求,需要在 Project Settings 中开启 /// [Project Settings -> Player -> Allow downloads over HTTP* 设置为 Always allowed] /// </summary> public sealed class WebRequestHelper { // 设置请求头 private static void SetRequestHeader(UnityWebRequest request, Dictionary<string, string> header = null) { if (request == null) return; request.SetRequestHeader("Content-Type", "application/json;charset=utf-8"); request.SetRequestHeader("Accept", "application/json"); request.SetRequestHeader("Authorization", ""); if (header == null) return; foreach (string key in header.Keys) { string value = header[key]; if (value == null) continue; request.SetRequestHeader(key, value); } } // 解析 private static void ParseAsync(UnityWebRequestAsyncOperation async, Action<string> completedCallback = null, Action<string> errorCallback = null) { string error = async.webRequest.error; if (!string.IsNullOrEmpty(error)) { async.webRequest.Dispose(); if (errorCallback == null) Debug.LogErrorFormat("请求出错: {0}", error); errorCallback?.Invoke(error); } else { string text = async.webRequest.downloadHandler.text; async.webRequest.Dispose(); completedCallback?.Invoke(text); } } // 采用GET,向后端请求数据 public static UnityWebRequestAsyncOperation Get(string url, Dictionary<string, string> header = null) { DownloadHandler downloadHandler = new DownloadHandlerBuffer(); UnityWebRequest request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbGET); request.downloadHandler = downloadHandler; SetRequestHeader(request, header); request.timeout = 5; request.chunkedTransfer = false; UnityWebRequestAsyncOperation async = request.SendWebRequest(); return async; } public static IEnumerator GetAsync(string url, Dictionary<string, string> header = null, Action<string> completedCallback = null, Action<string> errorCallback = null, Action<float> progressCallback = null) { yield return null; UnityWebRequestAsyncOperation async = Get(url, header); while (!async.isDone) { progressCallback?.Invoke(async.progress); yield return new WaitForNextFrameUnit(); } ParseAsync(async, completedCallback, errorCallback); } // 采用POST,向后端发送原始数据 public static UnityWebRequestAsyncOperation Post(string url, byte[] rawData, Dictionary<string, string> header = null) { DownloadHandler downloadHandler = new DownloadHandlerBuffer(); UploadHandler uploadHandler = new UploadHandlerRaw(rawData); UnityWebRequest request = new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST); request.downloadHandler = downloadHandler; request.uploadHandler = uploadHandler; SetRequestHeader(request, header); request.timeout = 5; UnityWebRequestAsyncOperation async = request.SendWebRequest(); return async; } public static UnityWebRequestAsyncOperation Post(string url, string rawData, Dictionary<string, string> header = null) { byte[] postBytes = Encoding.UTF8.GetBytes(rawData); return Post(url, postBytes, header); } public static IEnumerator PostAsync(string url, string rawData, Dictionary<string, string> header, Action<string> completedCallback = null, Action<string> errorCallback = null, Action<float> progressCallback = null) { yield return null; UnityWebRequestAsyncOperation async = Post(url, rawData, header); while (!async.isDone) { progressCallback?.Invoke(async.progress); yield return new WaitForNextFrameUnit(); } ParseAsync(async, completedCallback, errorCallback); } // 表单请求 POST public static IEnumerator PostFormAsync(string url, WWWForm form, Dictionary<string, string> header, Action<string> completedCallback = null, Action<string> errorCallback = null, Action<float> progressCallback = null) { yield return null; UnityWebRequest request = UnityWebRequest.Post(url, form); SetRequestHeader(request, header); UnityWebRequestAsyncOperation async = request.SendWebRequest(); while (!async.isDone) { progressCallback?.Invoke(async.progress); yield return new WaitForNextFrameUnit(); } ParseAsync(async, completedCallback, errorCallback); } //默认请求头 public static Dictionary<string, string> GetDefaultRequestHeader() { Dictionary<string, string> header = new Dictionary<string, string>(); header.Add("Content-Type", "application/json;charset=UTF-8"); header.Add("Access-Control-Allow-0rigin", "*"); header.Add("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE, PATCH"); header.Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); return header; } // 异步加载文本 public static IEnumerator LoadTextAsync(string url, Action<string> completedCallback = null, Action<string> errorCallback = null, Action<float> progressCallback = null) { using (UnityWebRequest webRequest = UnityWebRequest.Get(url)) { webRequest.SendWebRequest(); while (!webRequest.isDone) { progressCallback?.Invoke(webRequest.downloadProgress); yield return null; } if (webRequest.result == UnityWebRequest.Result.ConnectionError /*webRequest.isNetworkError*/ || webRequest.result == UnityWebRequest.Result.ProtocolError/*webRequest.isHttpError*/) { Debug.LogErrorFormat("{0}\n{1}", webRequest.error.ToString(), url); errorCallback?.Invoke(webRequest.error); } else { var text = webRequest.downloadHandler.text; completedCallback?.Invoke(text); } } } // 异步加载图片 public static IEnumerator LoadTextureAsync(string url, Action<Texture2D> completedCallback = null, Action<string> errorCallback = null, Action<float> progressCallback = null) { using (UnityWebRequest webRequest = UnityWebRequestTexture.GetTexture(url)) { webRequest.SendWebRequest(); while (!webRequest.isDone) { progressCallback?.Invoke(webRequest.downloadProgress); yield return null; } if (webRequest.result == UnityWebRequest.Result.ConnectionError /*webRequest.isNetworkError*/ || webRequest.result == UnityWebRequest.Result.ProtocolError/*webRequest.isHttpError*/) { Debug.LogErrorFormat("{0}\n{1}", webRequest.error.ToString(), url); errorCallback?.Invoke(webRequest.error); } else { var texture = DownloadHandlerTexture.GetContent(webRequest); completedCallback?.Invoke(texture); } } } // 异步加载 AudioClip public static IEnumerator LoadAudioAsync(string url, Action<AudioClip> completedCallback = null, Action<string> errorCallback = null, Action<float> progressCallback = null) { string ext = Path.GetExtension(url).ToLower(); AudioType audioType = AudioType.WAV; switch (ext) { case ".wav": audioType = AudioType.WAV; break; case ".mp3": audioType = AudioType.MPEG; break; } using (UnityWebRequest webRequest = UnityWebRequestMultimedia.GetAudioClip(url, audioType)) { ((DownloadHandlerAudioClip)webRequest.downloadHandler).streamAudio = true; webRequest.SendWebRequest(); while (!webRequest.isDone) { progressCallback?.Invoke(webRequest.downloadProgress); yield return null; } if (webRequest.result == UnityWebRequest.Result.ConnectionError /*webRequest.isNetworkError*/ || webRequest.result == UnityWebRequest.Result.ProtocolError/*webRequest.isHttpError*/) { Debug.LogErrorFormat("{0}\n{1}", webRequest.error.ToString(), url); errorCallback?.Invoke(webRequest.error); } else { var audioClip = DownloadHandlerAudioClip.GetContent(webRequest); completedCallback?.Invoke(audioClip); } } } // 异步加载 AssetBundle public static IEnumerator LoadAssetBundleAsync(string url, Action<AssetBundle> completedCallback = null, Action<string> errorCallback = null, Action<float> progressCallback = null) { using (UnityWebRequest webRequest = UnityWebRequestAssetBundle.GetAssetBundle(url)) { ((DownloadHandlerAssetBundle)webRequest.downloadHandler).autoLoadAssetBundle = false; webRequest.SendWebRequest(); while (!webRequest.isDone) { progressCallback?.Invoke(webRequest.downloadProgress); yield return null; } if (webRequest.result == UnityWebRequest.Result.ConnectionError /*webRequest.isNetworkError*/ || webRequest.result == UnityWebRequest.Result.ProtocolError/*webRequest.isHttpError*/) { Debug.LogErrorFormat("{0}\n{1}", webRequest.error.ToString(), url); errorCallback?.Invoke(webRequest.error); } else { var assetBundle = DownloadHandlerAssetBundle.GetContent(webRequest); completedCallback?.Invoke(assetBundle); assetBundle.Unload(false); } } } // 上传图片 public static IEnumerator UploadTexture(string url, string filename, Dictionary<string, string> formField, Texture2D tex, Action<string> completedCallback = null, Action<string> errorCallback = null, Action<float> progressCallback = null) { yield return null; if (tex == null) { Debug.LogError("上传的 Texture=null"); yield break; } if (string.IsNullOrEmpty(filename)) { Debug.LogError("上传的 filename=null"); yield break; } //如果在非主线程调用EncodeToJPG()会报以下错。 //UnityException: EncodeToJPG can only be called from the main thread. byte[] bytes = tex.EncodeToJPG(); WWWForm form = new WWWForm(); form.headers["Content-Type"] = "image/jpeg"; form.AddBinaryData("file", bytes, $"{filename}"); //添加更多字段参数 if (formField != null) { foreach (var key in formField.Keys) { string value = formField[key]; if (!string.IsNullOrEmpty(value)) form.AddField(key, formField[key]); } } UnityWebRequest request = UnityWebRequest.Post(url, form); UnityWebRequestAsyncOperation async = request.SendWebRequest(); while (!async.isDone) { progressCallback?.Invoke(async.progress); yield return new WaitForNextFrameUnit(); } ParseAsync(async, completedCallback, errorCallback); } }