using System; using System.IO; using System.Threading; using System.Threading.Tasks; using Assets.Scenes.Ride.Scripts; using Assets.Scripts.Apis.Models; using Cysharp.Threading.Tasks; using UnityEngine.Networking; namespace Assets.AR { public class ARDownloader { private MapRoute _route; private readonly string _localARDataPath; private readonly string _localARRoutePath; private readonly string _localVideoPath; private readonly string _serverARDataPath; private readonly string _serverARRoutePath; private readonly string _serverVideoPath; public bool IsVideoDownloaded { get; private set; } public bool IsARDataDownloaded { get; private set; } public bool IsARRouteDownloaded { get; private set; } public float Total { get; private set; } = 0; public ARDownloader(MapRoute route) { this._route = route; var dataPath = $"{PFConstants.ARFolder}/{_route.Id}"; var path = PFConstants.VideoFolder; Helper.CreateDirectoryIfNotExsit(dataPath); _localARDataPath = $"{dataPath}/{_route.Id}.json"; _localARRoutePath = $"{dataPath}/route-{_route.Id}.json"; _localVideoPath = $"{path}/{_route.FileName}"; _serverARDataPath = _route.ARConfig; _serverARRoutePath = _route.VideoRoute; _serverVideoPath = _route.Url; } public async UniTask DownLoadVideoAsync(IProgress progress = null, CancellationTokenSource cancellation = default(CancellationTokenSource)) { return await DownloadToFileAsync(_serverVideoPath, _localVideoPath, progress, cancellation); } public async UniTask DownLoadARRouteAsync(IProgress progress = null, CancellationTokenSource cancellation = default(CancellationTokenSource)) { return await DownloadToFileAsync(_serverARRoutePath, _localARRoutePath, progress, cancellation); } public async UniTask DownLoadARDataAsync(IProgress progress = null, CancellationTokenSource cancellation = default(CancellationTokenSource)) { return await DownloadToFileAsync(_serverARDataPath, _localARDataPath, progress, cancellation); } public async Task CheckAllAsync() { IsARRouteDownloaded = await CheckARRouteAsync(); IsARDataDownloaded = await CheckARDataAsync(); IsVideoDownloaded = await CheckVideoAsync(); if (IsVideoDownloaded) Total += 1f; if (IsARRouteDownloaded) Total += 1f; if (IsARDataDownloaded) Total += 1f; return IsVideoDownloaded || IsARRouteDownloaded || IsARDataDownloaded; } /// /// return true if the video is needed to download /// /// public async Task CheckVideoAsync() { try { if (!File.Exists(_localVideoPath)) return true; var result = await HeadFileAsync(_serverVideoPath, _localVideoPath); return result.responseCode != 304; } catch (Exception e) { return false; } } /// /// return true if the video is needed to download /// /// public async Task CheckARRouteAsync() { if (!File.Exists(_localARRoutePath)) return true; var result = await HeadFileAsync(_serverARRoutePath, _localARRoutePath); return result.responseCode != 304; } /// /// return true if the video is needed to download /// /// public async Task CheckARDataAsync() { if (!File.Exists(_localARDataPath)) return true; var result = await HeadFileAsync(_serverARDataPath, _localARDataPath); return result.responseCode != 304; } /// /// 下载文件到本地(持续存入磁盘减少内存占用) /// /// 文件连接 /// 本地文件全路径 /// 下载进度 public static async UniTask DownloadToFileAsync(string downloadUrl, string fullPath, IProgress progress = null, CancellationTokenSource cancellation = default(CancellationTokenSource)) { var dh = new DownloadHandlerFile(fullPath) { removeFileOnAbort = true }; var request = UnityWebRequest.Get(downloadUrl); request.downloadHandler = dh; request.method = UnityWebRequest.kHttpVerbGET; var result = await request.SendWebRequest().ToUniTask(progress, PlayerLoopTiming.Update, (cancellation?.Token ?? default(CancellationToken))); var etag = result.GetResponseHeader("ETag"); var path = fullPath + ".etag"; if (!File.Exists(path)) { File.Create(path); } File.WriteAllText(path, etag); return result; } /// /// head request for file meta info such as etag. /// /// download url /// local path for storage /// public static async UniTask HeadFileAsync(string downloadUrl, string fullPath, IProgress progress = null, CancellationTokenSource cancellation = default(CancellationTokenSource)) { try { var etagFile = fullPath + ".etag"; var etag = string.Empty; if (File.Exists(etagFile)) { etag = File.ReadAllText(etagFile); } var request = UnityWebRequest.Head(downloadUrl); if (!string.IsNullOrEmpty(etag)) request.SetRequestHeader("If-None-Match", etag); var result = await request .SendWebRequest().ToUniTask(progress, PlayerLoopTiming.Update, (cancellation?.Token ?? default(CancellationToken))); if (result.responseCode != 304) { etag = result.GetResponseHeader("ETag"); File.WriteAllText(fullPath + ".etag", etag); } return result; } catch (Exception e) { return null; } } } }