12 private const int timeoutMs = 30 * 1000;
13 private SynchronizationContext _context;
14 private const int DefaultDownloadBufferLength = 65536;
15 List<WebRequest> requests =
new List<WebRequest>();
19 _context = SynchronizationContext.Current ??
new SynchronizationContext();
22 public long GetURLFileSize(
string address)
24 return GetURLFileSize(
new Uri(address));
27 public long GetURLFileSize(Uri address)
29 WebRequest request = GetWebRequest(address);
30 request.Method =
"HEAD";
31 WebResponse response = request.GetResponse();
32 return response.ContentLength;
35 public Task DownloadFileTaskAsyncResume(Uri address,
string fileName,
bool resume =
false, Callback<float> progressCallback =
null)
37 var tcs =
new TaskCompletionSource<object>(address);
43 FileMode filemode = FileMode.Create;
46 var fileInfo =
new FileInfo(fileName);
47 if (fileInfo.Exists) bytesToSkip = fileInfo.Length;
50 WebRequest request = GetWebRequest(address);
51 if (request is HttpWebRequest webRequest && bytesToSkip > 0)
53 long remoteFileSize = GetURLFileSize(address);
54 if (bytesToSkip >= remoteFileSize)
56 LLMUnitySetup.Log($
"File is already fully downloaded: {fileName}");
57 tcs.TrySetResult(
true);
61 filemode = FileMode.Append;
62 LLMUnitySetup.Log($
"File exists at {fileName}, skipping {bytesToSkip} bytes");
63 webRequest.AddRange(bytesToSkip);
64 webRequest.ReadWriteTimeout = timeoutMs;
67 fs =
new FileStream(fileName, filemode, FileAccess.Write);
68 DownloadBitsAsync(request, fs, bytesToSkip, progressCallback, tcs);
73 tcs.TrySetException(e);
79 public void CancelDownloadAsync()
81 LLMUnitySetup.Log(
"Cancellation requested, aborting download.");
82 foreach (WebRequest request
in requests) AbortRequest(request);
86 public void AbortRequest(WebRequest request)
94 LLMUnitySetup.LogError($
"Error aborting request: {e.Message}");
98 private async
void DownloadBitsAsync(WebRequest request, Stream writeStream,
long bytesToSkip = 0, Callback<float> progressCallback =
null, TaskCompletionSource<object> tcs =
null)
102 requests.Add(request);
103 WebResponse response = await request.GetResponseAsync().ConfigureAwait(
false);
105 long contentLength = response.ContentLength;
106 byte[] copyBuffer =
new byte[contentLength == -1 || contentLength > DefaultDownloadBufferLength ? DefaultDownloadBufferLength : contentLength];
108 long TotalBytesToReceive = Math.Max(contentLength, 0) + bytesToSkip;
109 long BytesReceived = bytesToSkip;
112 using (Stream readStream = response.GetResponseStream())
114 if (readStream !=
null)
118 int bytesRead = await readStream.ReadAsync(
new Memory<byte>(copyBuffer)).ConfigureAwait(
false);
124 BytesReceived += bytesRead;
125 if (BytesReceived != TotalBytesToReceive)
127 PostProgressChanged(progressCallback, BytesReceived, TotalBytesToReceive);
130 await writeStream.WriteAsync(
new ReadOnlyMemory<byte>(copyBuffer, 0, bytesRead)).ConfigureAwait(
false);
134 if (TotalBytesToReceive < 0)
136 TotalBytesToReceive = BytesReceived;
138 PostProgressChanged(progressCallback, BytesReceived, TotalBytesToReceive);
140 tcs.TrySetResult(
true);
144 tcs.TrySetException(e);
146 AbortRequest(request);
147 tcs.TrySetResult(
false);
151 writeStream?.Close();
152 requests.Remove(request);
156 private void PostProgressChanged(Callback<float> progressCallback,
long BytesReceived,
long TotalBytesToReceive)
158 if (progressCallback !=
null && BytesReceived > 0)
160 float progressPercentage = TotalBytesToReceive < 0 ? 0 : TotalBytesToReceive == 0 ? 1 : (float)BytesReceived / TotalBytesToReceive;
161 _context.Post(_ => progressCallback?.Invoke(progressPercentage),
null);