ASP.NET在创建BitmapImage时抛出Win32Exception异常,无法找到指定的文件。

4
我正在开发一个书籍浏览网站,它会在提供图片之前将较大的图片(高达77+百万像素)进行缩放。它基本上执行以下算法:
  1. 如果已经缓存了所请求的书籍,请提供缓存的图像。
  2. 如果尚未缓存该书籍,则将该书籍添加到我们的书籍缓存线程池中。
  3. 书籍缓存线程池将每个书籍页面添加到我们的页面缓存线程池中。
  4. 页面缓存线程池通过反复缩小图像的大小,将位于局域网服务器上的图像缓存为2x、4x、8x、16x、32x和64x的版本。
下面是减半单个图像的代码。请注意使用BitmapImage:
Private Shared Function HalveImage(ByVal baseImagePath As String, ByVal baseWidth As Integer, ByVal baseHeight As Integer, ByVal newImagePath As String) As BitmapImage
    Dim bi As New BitmapImage

    With bi
        .BeginInit()
        .UriSource = New Uri(baseImagePath)
        .DecodePixelWidth = baseWidth / 2 'only seting one DecodePixelXXX property preserves the aspect ratio
        .EndInit()
    End With

    Dim enc As New System.Windows.Media.Imaging.JpegBitmapEncoder
    With enc
        .QualityLevel = 50
        .Frames.Add(BitmapFrame.Create(bi))
    End With

    Using stream As New IO.FileStream(newImagePath, IO.FileMode.Create)
        enc.Save(stream)
    End Using

    Return bi
End Function

这段代码在我的开发机上运行得很好,但是当我把它安装到服务器上时,服务器会突然停止缓存图片,即使已经缓存了数百张。网站的其余部分仍然正常工作。我们发现,页面缓存代码最终每次尝试创建BitmapImage对象时都会抛出此异常:

System.ComponentModel.Win32Exception: The system cannot find the file specified
   at MS.Win32.UnsafeNativeMethods.RegisterClassEx(WNDCLASSEX_D wc_d)
   at MS.Win32.HwndWrapper..ctor(Int32 classStyle, Int32 style, Int32 exStyle, Int32 x, Int32 y, Int32 width, Int32 height, String name, IntPtr parent, HwndWrapperHook[] hooks)
   at MS.Win32.MessageOnlyHwndWrapper..ctor()
   at System.Windows.Threading.Dispatcher..ctor()
   at System.Windows.Threading.Dispatcher.get_CurrentDispatcher()
   at System.Windows.Freezable..ctor()
   at System.Windows.Media.Imaging.BitmapSource..ctor(Boolean useVirtuals)
   at System.Windows.Media.Imaging.BitmapImage..ctor()
   at RemoteFSTester.PageCachingThreadPool.WICResizer.CachePage(String id, Int32 item, Int32 index) in C:\Users\[...]\PageCachingThreadPool.vb:line 127
   at RemoteFSTester.PageCachingThreadPool.CachePage(String id, Int32 item, Int32 index) in C:\Users\[...]\PageCachingThreadPool.vb:line 103
   at RemoteFSTester.PageCachingThreadPool.WorkerMethod(PriorityThreadPoolDelegateArgs args) in C:\Users\[...]\PageCachingThreadPool.vb:line 91
   at MDSA.Util.PriorityThreadpool.PriorityThreadPoolBase.Thread_DoWork(PriorityThreadPoolDelegateArgs args) in C:\Users\[...]\PriorityThreadPoolBase.vb:line 199

尽管异常显示“找不到指定的文件”,但我可以直接进入基础图像所在的位置并打开文件。
注意:搭载代码和缓存图像的服务器不是搭载基础图像的服务器。代码服务器通过类似“\servername\path\filename.jpg”的URI从基础图像服务器获取文件。
运行一些测试后,只有在尝试通过BitmapImage对象打开我们的IIS服务器上的图像时才会发生异常。无论我是通过UriSource设置文件路径还是创建一个FileStream对象并将其设置为BitmapImage的StreamSource属性,BitmapImage中的异常都会发生。如果通过FileStream对象打开文件,则不会发生异常,如果通过控制台应用程序打开文件也不会发生异常。使用BitmapImage对象的控制台应用程序也可以正常运行。
因此,最终问我的问题是,为什么服务器在通过ASP.NET缓存这些图像时出现问题,而我的开发机器没有问题?

在 .net 中使用内置的“loadFromFile”方法时,我遇到了各种类似的问题,通常与权限或锁定有关。通常是间歇性的,并且几乎总是导致失败的真正原因被掩盖了。因此,我们实施了一项策略,始终使用FileStream,并传递显式的FileMode和FileAccess参数。这样就摆脱了很多奇怪的问题。 - Tony Hopkinson
Tony,感谢你的建议,但将FileStream传递给BitmapImage对象(通过设置StreamSource而不是UriSource)并没有改变任何东西。 - Ski
你说过如果通过FileStream对象打开它可以工作,是吗? - Tony Hopkinson
如果我将每个文件作为FileStream对象打开,我可以遍历基本图像,但如果我将每个文件作为FileStream对象打开,然后将BitmapImage的StreamSource属性设置为FileStream对象,则无法遍历。 如果它能够这样工作,我就会使用StreamSource而不是UriSource,并且一天结束。 - Ski
啊,我明白了。这真是个难题。你考虑过使用FileStream将文件移动到缓存(或某些中间缓存)中,然后在本地进行位图图像处理吗? - Tony Hopkinson
1个回答

4
我在这里找到了解决问题的方法: Microsoft Connect 反馈 简而言之,我必须在每个线程进程结束时添加以下代码:
Dim myDispatcher As Dispatcher = Dispatcher.CurrentDispatcher
myDispatcher.BeginInvokeShutdown(DispatcherPriority.Normal)
Dispatcher.Run()

1
Sheesh Dispatcher被设计成一个进程,只要我们正在极快地使用资源,它就会一直运行下去。这让你想尖叫,不是吗? - Tony Hopkinson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接