@@ -277,7 +277,7 @@ private async Task ProcessRequestAsync(
277277
278278 if ( ! readResult . IsNewOrUpdated )
279279 {
280- await this . SendResponseAsync ( imageContext , key , readResult . CacheImageMetadata , readResult . Resolver ) ;
280+ await this . SendResponseAsync ( imageContext , key , readResult . CacheImageMetadata , readResult . Resolver , null ) ;
281281 return ;
282282 }
283283
@@ -288,104 +288,92 @@ private async Task ProcessRequestAsync(
288288 // This reduces the overheads of unnecessary processing.
289289 ImageWorkerResult writeResult ;
290290
291- using ( await this . asyncKeyLock . WriterLockAsync ( key ) )
291+ RecyclableMemoryStream outStream = null ;
292+ try
292293 {
293- RecyclableMemoryStream outStream = null ;
294- try
294+ using ( await this . asyncKeyLock . WriterLockAsync ( key ) )
295295 {
296- ImageCacheMetadata cachedImageMetadata = default ;
297- outStream = new RecyclableMemoryStream ( this . options . MemoryStreamManager ) ;
298- IImageFormat format ;
299-
300- // 14.9.3 CacheControl Max-Age
301- // Check to see if the source metadata has a CacheControl Max-Age value
302- // and use it to override the default max age from our options.
303- TimeSpan maxAge = this . options . BrowserMaxAge ;
304- if ( ! sourceImageMetadata . CacheControlMaxAge . Equals ( TimeSpan . MinValue ) )
305- {
306- maxAge = sourceImageMetadata . CacheControlMaxAge ;
307- }
308-
309- using ( Stream inStream = await sourceImageResolver . OpenReadAsync ( ) )
296+ try
310297 {
311- // No commands? We simply copy the stream across.
312- if ( commands . Count == 0 )
298+ ImageCacheMetadata cachedImageMetadata = default ;
299+ outStream = new RecyclableMemoryStream ( this . options . MemoryStreamManager ) ;
300+ IImageFormat format ;
301+
302+ // 14.9.3 CacheControl Max-Age
303+ // Check to see if the source metadata has a CacheControl Max-Age value
304+ // and use it to override the default max age from our options.
305+ TimeSpan maxAge = this . options . BrowserMaxAge ;
306+ if ( ! sourceImageMetadata . CacheControlMaxAge . Equals ( TimeSpan . MinValue ) )
313307 {
314- await inStream . CopyToAsync ( outStream ) ;
315- outStream . Position = 0 ;
316- format = await Image . DetectFormatAsync ( this . options . Configuration , outStream ) ;
308+ maxAge = sourceImageMetadata . CacheControlMaxAge ;
317309 }
318- else
310+
311+ using ( Stream inStream = await sourceImageResolver . OpenReadAsync ( ) )
319312 {
320- using var image = FormattedImage . Load ( this . options . Configuration , inStream ) ;
313+ // No commands? We simply copy the stream across.
314+ if ( commands . Count == 0 )
315+ {
316+ await inStream . CopyToAsync ( outStream ) ;
317+ outStream . Position = 0 ;
318+ format = await Image . DetectFormatAsync ( this . options . Configuration , outStream ) ;
319+ }
320+ else
321+ {
322+ using var image = FormattedImage . Load ( this . options . Configuration , inStream ) ;
321323
322- image . Process (
323- this . logger ,
324- this . processors ,
325- commands ,
326- this . commandParser ,
327- this . parserCulture ) ;
324+ image . Process (
325+ this . logger ,
326+ this . processors ,
327+ commands ,
328+ this . commandParser ,
329+ this . parserCulture ) ;
328330
329- await this . options . OnBeforeSaveAsync . Invoke ( image ) ;
331+ await this . options . OnBeforeSaveAsync . Invoke ( image ) ;
330332
331- image . Save ( outStream ) ;
332- format = image . Format ;
333+ image . Save ( outStream ) ;
334+ format = image . Format ;
335+ }
333336 }
334- }
335-
336- // Allow for any further optimization of the image.
337- outStream . Position = 0 ;
338- string contentType = format . DefaultMimeType ;
339- string extension = this . formatUtilities . GetExtensionFromContentType ( contentType ) ;
340- await this . options . OnProcessedAsync . Invoke ( new ImageProcessingContext ( context , outStream , commands , contentType , extension ) ) ;
341- outStream . Position = 0 ;
342-
343- cachedImageMetadata = new ImageCacheMetadata (
344- sourceImageMetadata . LastWriteTimeUtc ,
345- DateTime . UtcNow ,
346- contentType ,
347- maxAge ,
348- outStream . Length ) ;
349-
350- // Save the image to the cache and send the response to the caller.
351- await this . cache . SetAsync ( key , outStream , cachedImageMetadata ) ;
352-
353- // Remove any resolver from the cache so we always resolve next request
354- // for the same key.
355- CacheResolverLru . TryRemove ( key ) ;
356-
357- // Place the resolver in the lru cache.
358- ( IImageCacheResolver ImageCacheResolver , ImageCacheMetadata ImageCacheMetadata ) cachedImage = await
359- CacheResolverLru . GetOrAddAsync (
360- key ,
361- async k =>
362- {
363- IImageCacheResolver resolver = await this . cache . GetAsync ( k ) ;
364- ImageCacheMetadata metadata = default ;
365- if ( resolver != null )
366- {
367- metadata = await resolver . GetMetaDataAsync ( ) ;
368- }
369-
370- return ( resolver , metadata ) ;
371- } ) ;
372337
373- writeResult = new ImageWorkerResult ( cachedImage . ImageCacheMetadata , cachedImage . ImageCacheResolver ) ;
374- }
375- catch ( Exception ex )
376- {
377- // Log the error internally then rethrow.
378- // We don't call next here, the pipeline will automatically handle it
379- this . logger . LogImageProcessingFailed ( imageContext . GetDisplayUrl ( ) , ex ) ;
380- throw ;
381- }
382- finally
383- {
384- await this . StreamDisposeAsync ( outStream ) ;
338+ // Allow for any further optimization of the image.
339+ outStream . Position = 0 ;
340+ string contentType = format . DefaultMimeType ;
341+ string extension = this . formatUtilities . GetExtensionFromContentType ( contentType ) ;
342+ await this . options . OnProcessedAsync . Invoke ( new ImageProcessingContext ( context , outStream , commands , contentType , extension ) ) ;
343+ outStream . Position = 0 ;
344+
345+ cachedImageMetadata = new ImageCacheMetadata (
346+ sourceImageMetadata . LastWriteTimeUtc ,
347+ DateTime . UtcNow ,
348+ contentType ,
349+ maxAge ,
350+ outStream . Length ) ;
351+
352+ // Save the image to the cache and send the response to the caller.
353+ await this . cache . SetAsync ( key , outStream , cachedImageMetadata ) ;
354+ outStream . Position = 0 ;
355+
356+ // Remove any resolver from the cache so we always resolve next request
357+ // for the same key.
358+ CacheResolverLru . TryRemove ( key ) ;
359+
360+ writeResult = new ImageWorkerResult ( cachedImageMetadata , null ) ;
361+ }
362+ catch ( Exception ex )
363+ {
364+ // Log the error internally then rethrow.
365+ // We don't call next here, the pipeline will automatically handle it
366+ this . logger . LogImageProcessingFailed ( imageContext . GetDisplayUrl ( ) , ex ) ;
367+ throw ;
368+ }
385369 }
386- }
387370
388- await this . SendResponseAsync ( imageContext , key , writeResult . CacheImageMetadata , writeResult . Resolver ) ;
371+ await this . SendResponseAsync ( imageContext , key , writeResult . CacheImageMetadata , writeResult . Resolver , outStream ) ;
372+ }
373+ finally
374+ {
375+ await this . StreamDisposeAsync ( outStream ) ;
376+ }
389377 }
390378
391379 private ValueTask StreamDisposeAsync ( Stream stream )
@@ -411,8 +399,8 @@ private ValueTask StreamDisposeAsync(Stream stream)
411399 }
412400
413401 private async Task < ImageWorkerResult > IsNewOrUpdatedAsync (
414- IImageResolver sourceImageResolver ,
415- string key )
402+ IImageResolver sourceImageResolver ,
403+ string key )
416404 {
417405 // Get the source metadata for processing, storing the result for future checks.
418406 ImageMetadata sourceImageMetadata = await
@@ -464,7 +452,8 @@ private async Task SendResponseAsync(
464452 ImageContext imageContext ,
465453 string key ,
466454 ImageCacheMetadata metadata ,
467- IImageCacheResolver cacheResolver )
455+ IImageCacheResolver cacheResolver ,
456+ Stream stream )
468457 {
469458 imageContext . ComprehendRequestHeaders ( metadata . CacheLastWriteTimeUtc , metadata . ContentLength ) ;
470459
@@ -480,11 +469,19 @@ private async Task SendResponseAsync(
480469
481470 this . logger . LogImageServed ( imageContext . GetDisplayUrl ( ) , key ) ;
482471
483- // When stream is null we're sending from the cache.
484- using ( Stream stream = await cacheResolver . OpenReadAsync ( ) )
472+ // If stream is not null, then send it directly. Otherwise, use the cacheResolver
473+ // to load from the cache.
474+ if ( stream != null )
485475 {
486476 await imageContext . SendAsync ( stream , metadata ) ;
487477 }
478+ else
479+ {
480+ using ( Stream cacheStream = await cacheResolver . OpenReadAsync ( ) )
481+ {
482+ await imageContext . SendAsync ( cacheStream , metadata ) ;
483+ }
484+ }
488485
489486 return ;
490487
0 commit comments