18 private const int timeoutMs = 30 * 1000;
19 private SynchronizationContext _context;
20 private const int DefaultDownloadBufferLength = 65536;
21 List<WebRequest> requests =
new List<WebRequest>();
25 _context = SynchronizationContext.Current ??
new SynchronizationContext();
28 public long GetURLFileSize(
string address)
30 return GetURLFileSize(
new Uri(address));
33 public long GetURLFileSize(Uri address)
35 WebRequest request = GetWebRequest(address);
36 request.Method =
"HEAD";
37 WebResponse response = request.GetResponse();
38 return response.ContentLength;
41 public Task DownloadFileTaskAsyncResume(Uri address,
string fileName,
bool resume =
false, Callback<float> progressCallback =
null)
43 var tcs =
new TaskCompletionSource<object>(address);
49 FileMode filemode = FileMode.Create;
52 var fileInfo =
new FileInfo(fileName);
53 if (fileInfo.Exists) bytesToSkip = fileInfo.Length;
56 WebRequest request = GetWebRequest(address);
57 if (request is HttpWebRequest webRequest && bytesToSkip > 0)
59 long remoteFileSize = GetURLFileSize(address);
60 if (bytesToSkip >= remoteFileSize)
62 LLMUnitySetup.Log($
"File is already fully downloaded: {fileName}");
63 tcs.TrySetResult(
true);
67 filemode = FileMode.Append;
68 LLMUnitySetup.Log($
"File exists at {fileName}, skipping {bytesToSkip} bytes");
69 webRequest.AddRange(bytesToSkip);
70 webRequest.ReadWriteTimeout = timeoutMs;
73 fs =
new FileStream(fileName, filemode, FileAccess.Write);
74 DownloadBitsAsync(request, fs, bytesToSkip, progressCallback, tcs);
79 tcs.TrySetException(e);
85 public void CancelDownloadAsync()
87 LLMUnitySetup.Log(
"Cancellation requested, aborting download.");
88 foreach (WebRequest request
in requests) AbortRequest(request);
92 public void AbortRequest(WebRequest request)
100 LLMUnitySetup.LogError($
"Error aborting request: {e.Message}");
104 private async
void DownloadBitsAsync(WebRequest request, Stream writeStream,
long bytesToSkip = 0, Callback<float> progressCallback =
null, TaskCompletionSource<object> tcs =
null)
108 requests.Add(request);
109 WebResponse response = await request.GetResponseAsync().ConfigureAwait(
false);
111 long contentLength = response.ContentLength;
112 byte[] copyBuffer =
new byte[contentLength == -1 || contentLength > DefaultDownloadBufferLength ? DefaultDownloadBufferLength : contentLength];
114 long TotalBytesToReceive = Math.Max(contentLength, 0) + bytesToSkip;
115 long BytesReceived = bytesToSkip;
118 using (Stream readStream = response.GetResponseStream())
120 if (readStream !=
null)
124 int bytesRead = await readStream.ReadAsync(
new Memory<byte>(copyBuffer)).ConfigureAwait(
false);
130 BytesReceived += bytesRead;
131 if (BytesReceived != TotalBytesToReceive)
133 PostProgressChanged(progressCallback, BytesReceived, TotalBytesToReceive);
136 await writeStream.WriteAsync(
new ReadOnlyMemory<byte>(copyBuffer, 0, bytesRead)).ConfigureAwait(
false);
140 if (TotalBytesToReceive < 0)
142 TotalBytesToReceive = BytesReceived;
144 PostProgressChanged(progressCallback, BytesReceived, TotalBytesToReceive);
146 tcs.TrySetResult(
true);
150 tcs.TrySetException(e);
152 AbortRequest(request);
153 tcs.TrySetResult(
false);
157 writeStream?.Close();
158 requests.Remove(request);
162 private void PostProgressChanged(Callback<float> progressCallback,
long BytesReceived,
long TotalBytesToReceive)
164 if (progressCallback !=
null && BytesReceived > 0)
166 float progressPercentage = TotalBytesToReceive < 0 ? 0 : TotalBytesToReceive == 0 ? 1 : (float)BytesReceived / TotalBytesToReceive;
167 _context.Post(_ => progressCallback?.Invoke(progressPercentage),
null);