@@ -21,18 +21,18 @@ internal abstract class PackageContentLoader<TContent, TType> : IPackageContentL
2121 private readonly Dictionary < string , AsyncLazy < IPackageContent < TContent , TType > ? > > _runningDownloads = new ( StringComparer . OrdinalIgnoreCase ) ;
2222 private readonly IPackagesProviderFilesManager _filesManager ;
2323 private readonly IHttpClientFactory _httpClientFactory ;
24- private readonly string _downloadCacheFolder ;
2524 private readonly IPackagesProviderFileModification ? _modification ;
25+ private readonly PackageContentLoaderOptions _options ;
2626
2727 protected abstract IReadOnlyDictionary < string , TType > MimeTypeTypeMapping { get ; }
2828 protected abstract IReadOnlyDictionary < string , TType > FileExtensionTypeMapping { get ; }
2929
30- protected PackageContentLoader ( IPackagesProviderFilesManager filesManager , IHttpClientFactory httpClientFactory , string downloadCacheFolder , IPackagesProviderFileModification ? modification )
30+ protected PackageContentLoader ( IPackagesProviderFilesManager filesManager , IHttpClientFactory httpClientFactory , IPackagesProviderFileModification ? modification , PackageContentLoaderOptions options )
3131 {
3232 _filesManager = filesManager ;
3333 _httpClientFactory = httpClientFactory ;
34- _downloadCacheFolder = downloadCacheFolder ;
3534 _modification = modification ;
35+ _options = options ;
3636 }
3737
3838 public IPackageContent < TContent , TType > ? TryLoad ( ContentLoadMode loadMode , IPackageContentData ? contentData , CancellationToken cancellationToken )
@@ -145,26 +145,31 @@ private static bool HasUrl([NotNullWhen(true)] Uri? url)
145145 private async Task < IPackageContent < TContent , TType > ? > TryDownloadAsync ( Uri url , CancellationToken cancellationToken )
146146 {
147147 string urlMD5Hash = CalcMD5Hash ( url ) ;
148- string tempFilePath = Path . Combine ( _downloadCacheFolder , urlMD5Hash ) ;
148+ string tempFilePath = Path . Combine ( _options . DownloadCacheFolder , urlMD5Hash ) ;
149149
150- if ( ! Directory . Exists ( _downloadCacheFolder ) )
151- Directory . CreateDirectory ( _downloadCacheFolder ) ;
150+ Directory . CreateDirectory ( _options . DownloadCacheFolder ) ;
152151
153- lock ( urlMD5Hash )
152+ if ( ! File . Exists ( tempFilePath ) )
154153 {
155- if ( File . Exists ( tempFilePath ) )
156- {
157- cancellationToken . ThrowIfCancellationRequested ( ) ;
154+ TimeSpan timeout = _options . DownloadCacheAccessTimeout ;
155+ TimeSpan retryDelay = _options . DownloadCacheAccessRetryDelay ;
158156
159- TType type = GetContentType ( tempFilePath ) ;
157+ using ( await LockFile . LockAsync ( tempFilePath , timeout , retryDelay , cancellationToken ) )
158+ {
159+ if ( ! File . Exists ( tempFilePath ) )
160+ {
161+ cancellationToken . ThrowIfCancellationRequested ( ) ;
160162
161- return AddFileAndCreateContent ( tempFilePath , type ) ;
163+ return await TryDownloadToTempFileAsync ( url , tempFilePath , cancellationToken ) ;
164+ }
162165 }
163166 }
164167
165168 cancellationToken . ThrowIfCancellationRequested ( ) ;
166169
167- return await TryDownloadAsync ( url , tempFilePath , cancellationToken ) ;
170+ TType type = GetContentType ( tempFilePath ) ;
171+
172+ return AddFileAndCreateContent ( tempFilePath , type ) ;
168173 }
169174 private static string CalcMD5Hash ( Uri uri )
170175 {
@@ -179,7 +184,7 @@ private static string CalcMD5Hash(Uri uri)
179184 . ToLower ( ) ;
180185 }
181186 }
182- private async Task < IPackageContent < TContent , TType > ? > TryDownloadAsync ( Uri url , string tempFilePath , CancellationToken cancellationToken )
187+ private async Task < IPackageContent < TContent , TType > ? > TryDownloadToTempFileAsync ( Uri url , string tempFilePath , CancellationToken cancellationToken )
183188 {
184189 using HttpRequestMessage message = new ( HttpMethod . Get , url ) ;
185190
@@ -196,8 +201,13 @@ private static string CalcMD5Hash(Uri uri)
196201
197202 cancellationToken . ThrowIfCancellationRequested ( ) ;
198203
199- using ( Stream tempStream = File . Create ( tempFilePath ) )
200- await response . Content . CopyToAsync ( tempStream ) ;
204+ string uniqueTempFilePath = Path . Combine ( Path . GetDirectoryName ( tempFilePath ) , Guid . NewGuid ( ) . ToString ( "N" ) ) ;
205+
206+ using ( Stream fileStream = File . Create ( uniqueTempFilePath ) )
207+ await response . Content . CopyToAsync ( fileStream ) ;
208+
209+ File . Delete ( tempFilePath ) ;
210+ File . Move ( uniqueTempFilePath , tempFilePath ) ;
201211
202212 TType type = GetContentType ( response , tempFilePath ) ;
203213
0 commit comments