@@ -52,7 +52,7 @@ sealed class ImagePrefetcher<T> : IImageLoader
5252 private readonly T hostingPart ;
5353 private readonly IWebRequest resourceLoader ;
5454 private readonly HtmlImageInfoCollection prefetchedImages ;
55- private readonly object lockObject = new object ( ) ;
55+ private readonly object lockObject = new ( ) ;
5656 private readonly ImageProcessingMode processingMode ;
5757
5858
@@ -68,7 +68,7 @@ public ImagePrefetcher(T hostingPart, IWebRequest resourceLoader, ImageProcessin
6868 this . hostingPart = hostingPart ;
6969 this . resourceLoader = resourceLoader ;
7070 this . processingMode = processingMode ;
71- this . prefetchedImages = new HtmlImageInfoCollection ( ) ;
71+ this . prefetchedImages = [ ] ;
7272 }
7373
7474 //____________________________________________________________________
@@ -133,65 +133,38 @@ public ImagePrefetcher(T hostingPart, IWebRequest resourceLoader, ImageProcessin
133133 /// </summary>
134134 private async Task < HtmlImageInfo ? > DownloadRemoteImage ( string src , CancellationToken cancellationToken )
135135 {
136- Uri imageUri = new Uri ( src , UriKind . RelativeOrAbsolute ) ;
136+ Uri imageUri = new ( src , UriKind . RelativeOrAbsolute ) ;
137137 if ( imageUri . IsAbsoluteUri && ! resourceLoader . SupportsProtocol ( imageUri . Scheme ) )
138138 return null ;
139139
140- Resource ? response ;
141-
142- response = await resourceLoader . FetchAsync ( imageUri , cancellationToken ) . ConfigureAwait ( false ) ;
140+ using var response = await resourceLoader . FetchAsync ( imageUri , cancellationToken ) . ConfigureAwait ( false ) ;
143141 if ( response ? . Content == null )
144142 return null ;
145143
146- using ( response )
144+ // Copy the stream into a MemoryStream to avoid ObjectDisposedException
145+ using var buffer = new MemoryStream ( ) ;
146+ response . Content . CopyTo ( buffer ) ;
147+ buffer . Seek ( 0 , SeekOrigin . Begin ) ;
148+
149+ // For requested url with no filename, we need to read the media mime type if provided
150+ response . Headers . TryGetValue ( "Content-Type" , out var mime ) ;
151+ if ( ! TryInspectMimeType ( mime , out PartTypeInfo type )
152+ && ! TryGuessTypeFromUri ( imageUri , out type )
153+ && ! TryGuessTypeFromStream ( buffer , out type )
154+ )
147155 {
148- // For requested url with no filename, we need to read the media mime type if provided
149- response . Headers . TryGetValue ( "Content-Type" , out var mime ) ;
150- if ( ! TryInspectMimeType ( mime , out PartTypeInfo type )
151- && ! TryGuessTypeFromUri ( imageUri , out type )
152- && ! TryGuessTypeFromStream ( response . Content , out type ) )
153- {
154- return null ;
155- }
156-
157- // Generate a unique GUID-based relationship ID for the image part
158- string relationshipId = "img_" + Guid . NewGuid ( ) . ToString ( "N" ) ;
159-
160- // Synchronize access to AddImagePart to prevent concurrent modifications to the OpenXml document
161- ImagePart ipart ;
162- lock ( lockObject )
163- {
164- ipart = hostingPart . AddImagePart ( type , relationshipId ) ;
165- }
166-
167- Size originalSize ;
168- using ( var outputStream = ipart . GetStream ( FileMode . Create ) )
169- {
170- response . Content . CopyTo ( outputStream ) ;
171-
172- outputStream . Seek ( 0L , SeekOrigin . Begin ) ;
173- originalSize = GetImageSize ( outputStream ) ;
174- }
175-
176- string partId ;
177- lock ( lockObject )
178- {
179- partId = hostingPart . GetIdOfPart ( ipart ) ;
180- }
181-
182- return new HtmlImageInfo ( src , partId ) {
183- TypeInfo = type ,
184- Size = originalSize
185- } ;
156+ return null ;
186157 }
158+
159+ return SaveImageAssert ( src , type , buffer . CopyTo ) ;
187160 }
188161
189162 /// <summary>
190163 /// Create an external relationship to an image without downloading it.
191164 /// </summary>
192165 private HtmlImageInfo ? CreateExternalImageLink ( string src )
193166 {
194- Uri imageUri = new Uri ( src , UriKind . RelativeOrAbsolute ) ;
167+ Uri imageUri = new ( src , UriKind . RelativeOrAbsolute ) ;
195168
196169 // Resolve relative URIs if possible (only for DefaultWebRequest which has BaseImageUrl)
197170 if ( ! imageUri . IsAbsoluteUri && resourceLoader is DefaultWebRequest defaultWebRequest
@@ -220,8 +193,7 @@ public ImagePrefetcher(T hostingPart, IWebRequest resourceLoader, ImageProcessin
220193
221194 // Return image info with external flag set
222195 // Note: Size will be empty as we don't download the image
223- return new HtmlImageInfo ( src , relationshipId )
224- {
196+ return new HtmlImageInfo ( src , relationshipId ) {
225197 IsExternal = true ,
226198 Size = Size . Empty ,
227199 TypeInfo = ImagePartType . Png // Default type, actual type doesn't matter for external links
@@ -235,39 +207,37 @@ public ImagePrefetcher(T hostingPart, IWebRequest resourceLoader, ImageProcessin
235207 {
236208 if ( DataUri . TryCreate ( src , out var dataUri ) )
237209 {
238- Size originalSize ;
239210 knownContentType . TryGetValue ( dataUri ! . Mime , out PartTypeInfo type ) ;
240- // Generate a unique GUID-based relationship ID for the image part
241- string relationshipId = "img_" + Guid . NewGuid ( ) . ToString ( "N" ) ;
242-
243- // Synchronize access to AddImagePart to prevent concurrent modifications to the OpenXml document
244- ImagePart ipart ;
245- lock ( lockObject )
246- {
247- ipart = hostingPart . AddImagePart ( type , relationshipId ) ;
248- }
249-
250- using ( var outputStream = ipart . GetStream ( FileMode . Create ) )
251- {
252- outputStream . Write ( dataUri . Data , 0 , dataUri . Data . Length ) ;
253211
254- outputStream . Seek ( 0L , SeekOrigin . Begin ) ;
255- originalSize = GetImageSize ( outputStream ) ;
256- }
212+ return SaveImageAssert ( src , type , stream => stream . Write ( dataUri . Data , 0 , dataUri . Data . Length ) ) ;
213+ }
257214
258- string partId ;
259- lock ( lockObject )
260- {
261- partId = hostingPart . GetIdOfPart ( ipart ) ;
262- }
215+ return null ;
216+ }
263217
264- return new HtmlImageInfo ( src , partId ) {
265- TypeInfo = type ,
266- Size = originalSize
267- } ;
218+ private HtmlImageInfo SaveImageAssert ( string src , PartTypeInfo type , Action < Stream > writeImage )
219+ {
220+ ImagePart ipart ;
221+ string relationshipId = "img_" + Guid . NewGuid ( ) . ToString ( "N" ) ;
222+ lock ( lockObject )
223+ {
224+ ipart = hostingPart . AddImagePart ( type , relationshipId ) ;
268225 }
269226
270- return null ;
227+ Size originalSize ;
228+ using ( var outputStream = ipart . GetStream ( FileMode . Create ) )
229+ {
230+ writeImage ( outputStream ) ;
231+ outputStream . Seek ( 0L , SeekOrigin . Begin ) ;
232+ originalSize = GetImageSize ( outputStream ) ;
233+ }
234+
235+ string partId = hostingPart . GetIdOfPart ( ipart ) ;
236+ return new HtmlImageInfo ( src , partId )
237+ {
238+ TypeInfo = type ,
239+ Size = originalSize
240+ } ;
271241 }
272242
273243 //____________________________________________________________________
0 commit comments