在 MVC 中使用 sta 线程下载 png 图像

Download png image using a sta thread in MVC

在我的 MVC 应用程序中,我在 STA 线程中使用 Web 浏览器控件。我想在 STA 线程结束时向客户端发送一个 png 图像。

这是我的代码

         public void Export()    //Action method for exporting
         {
             //Saving widget as image using web browser control
             System.Threading.Thread tr = new System.Threading.Thread(() => ExportWidget());
             tr.SetApartmentState(ApartmentState.STA);
             tr.Start();
         }

         public void ExportWidget() //STA method for downloading
         {

             WebBrowser browser = new WebBrowser() { ScriptErrorsSuppressed=true, WebBrowserShortcutsEnabled = false, Size = new System.Drawing.Size(1000, 1000) };

             browser.DocumentText = new StringBuilder()
                 //Add header for HTML document
                 .Append("<!DOCTYPE html><html><head><meta http-equiv='X-UA-Compatible' content='IE=edge' />"

                 //Add scripts required for rendering widget in browser control
                 + AddScripts(browser, widgetOption)

                 + "</head><body><div id='widgetContainer'></div></body></html>").ToString();


             //Check browser is loaded or not. If it is not ready wait until browser loading is complete
             while (browser.ReadyState == WebBrowserReadyState.Loading)
             {
                 Application.DoEvents();
                 Thread.Sleep(5);
             }

             MemoryStream stream = new MemoryStream();

             using (Bitmap img = new Bitmap(ParseInt(bounds[1]), ParseInt(bounds[0])))
             {
                 browser.DrawToBitmap(img, new Rectangle(ParseInt(bounds[2]), ParseInt(bounds[3]), img.Width, img.Height));          
                 img.Save(stream, ImageFormat.Png);

                 browser.Dispose();                
             }

             Response.Clear();

             stream.WriteTo(Response.OutputStream);
             stream.Dispose();
             string fileName = "WidgetExport.png";
             Response.ContentType = "application/octet-stream";

             //System.ArgumentException throws here
             Response.AddHeader("Content-Disposition", "attachment;filename=" + fileName);
             Response.Flush();
         }

我正在检查在服务器端导出 JavaScript 小部件的可能性。

这是正确的做法吗?在 header

中设置 "Content-Disposition" 时,上面的代码抛出 System.ArgumentException

请分享您的建议

经过几个小时的分析,我想出了以下解决方案来完全在服务器端下载一个 JavaScript 小部件。

它并不优雅,但目前这是我所知道的唯一可行的解​​决方案

         public void Export()    //Action method for exporting
         {
             //Saving widget as image using web browser control
             System.Threading.Thread tr = new System.Threading.Thread(() => ExportWidget());
             tr.SetApartmentState(ApartmentState.STA);
             tr.Start();

             string fileName = Server.MapPath(@"\WidgetExport.png");            

             //Make current thread waits until widget image is ready in STA thread
             while(tr.IsAlive)
             {
                 Thread.Sleep(5);
             }

             //Read widget image as bytes
             byte[] file = System.IO.File.ReadAllBytes(fileName);

             //Delete the file after reading
             System.IO.File.Delete(fileName);

             //Allowing client to download the image
             Response.OutputStream.Write(file, 0, file.Length);
             Response.ContentType = "application/octet-stream";             
             Response.AddHeader("Content-Disposition", "attachment;filename=" + fileName);
             Response.Flush(); 
         }

     public void ExportWidget() //STA thread method for downloading
     {

         WebBrowser browser = new WebBrowser() { ScriptErrorsSuppressed=true, WebBrowserShortcutsEnabled = false, Size = new System.Drawing.Size(1000, 1000) };

         browser.DocumentText = new StringBuilder()
             //Add header for HTML document
             .Append("<!DOCTYPE html><html><head><meta http-equiv='X-UA-Compatible' content='IE=edge' />"

             //Add scripts required for rendering widget in browser control
             + AddScripts(browser, widgetOption)

             + "</head><body><div id='widgetContainer'></div></body></html>").ToString();


         //Check browser is loaded or not. If it is not ready wait until browser loading is complete
         while (browser.ReadyState == WebBrowserReadyState.Loading)
         {
             Application.DoEvents();
             Thread.Sleep(5);
         }

         MemoryStream stream = new MemoryStream();

         using (Bitmap img = new Bitmap(ParseInt(bounds[1]), ParseInt(bounds[0])))
         {
             browser.DrawToBitmap(img, new Rectangle(ParseInt(bounds[2]), ParseInt(bounds[3]), img.Width, img.Height));          
             img.Save(Server.MapPath(@"\WidgetExport.png"), ImageFormat.Png);

             browser.Dispose();                
         }
     }

以上代码执行以下操作

  1. 客户端请求到达服务器

  2. 服务器使用 C# 包装器填充 JavaScript 小部件

  3. WebBrowser 控件在action 方法的线程中不起作用,因为它只在STA 线程中起作用。应该停止操作方法的线程,直到 STA 线程完成

  4. 于是启动一个新的STA线程,并在其中创建WebBrowser控件。

  5. 在 WebBrowser 控件中添加 HTML 小部件所需的内容和脚本

  6. 将 C# 包装器序列化为等效的 JavaScript 作为字符串并将其添加到 WebBrowser 控件

  7. 使用 WebBrowser 控件的 InvokeScript 方法执行序列化的 JavaScript 代码。这将在 WebBrowser 控件中呈现小部件

  8. 使用WebBrowser 控件的DrawToBitmap 方法在Bitmap 中绘制JavaScript 小部件的图像

  9. 保存并处理位图。处理 WebBrowser 控件。 STA 线程的执行现在完成

  10. 现在操作方法线程应该可用了。从保存在 STA 线程中的小部件图像中获取数据

  11. 读取数据后从服务器删除图片

  12. 允许在客户端使用Response.OutputStream

  13. 下载图片