【问题标题】:taking screenshot of an iFrame on button click单击按钮时截取 iFrame 的屏幕截图
【发布时间】:2012-05-06 00:03:10
【问题描述】:

您好,我正在创建一个具有 iFrame 和按钮的网站。该按钮的功能是获取 iFrame 内显示的任何内容的屏幕截图并将其保存为硬盘上的图像。以下是我的代码使用

private void saveURLToImage(string url) 
    { 
        if (!string.IsNullOrEmpty(url)) 
        { 
            string content = ""; 


            System.Net.WebRequest webRequest = WebRequest.Create(url); 
            System.Net.WebResponse webResponse = webRequest.GetResponse(); 
            System.IO.StreamReader sr = new StreamReader(webResponse.GetResponseStream(), System.Text.Encoding.GetEncoding("UTF-8"));

            content = sr.ReadToEnd(); 
            //save to file 
            byte[] b = Convert.FromBase64String(content); 
            System.IO.MemoryStream ms = new System.IO.MemoryStream(b); 
            System.Drawing.Image img = System.Drawing.Image.FromStream(ms); 
            img.Save(@"c:\pic.jpg", System.Drawing.Imaging.ImageFormat.Jpeg); 


            img.Dispose(); 
            ms.Close(); 
        } 
    } 

这是按钮点击的代码

 protected void Button1_Click(object sender, ImageClickEventArgs e)
{

saveURLToImage("http://www.google.com");

}

但是当我点击按钮时出现错误

The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or a non-white space character among the padding characters.

在这条线上

byte[] b = Convert.FromBase64String(content);

我无法弄清楚如何解决它。任何帮助将不胜感激。谢谢您

【问题讨论】:

    标签: asp.net iframe buttonclick


    【解决方案1】:

    在您的情况下,content 是构成页面的原始 HTML,而不是它的呈现方式 - 这将由浏览器决定(在调试器中查看)所以,因为那不是base 64(这是一种仅使用 ASCII 字符对二进制数据进行编码的方法),为了使其工作,您需要获取 JPEG 编码图像的 base 64 编码二进制数据,但浏览器已呈现 HTML,它你没有。

    我认为这在 Web 应用程序中不是一件容易的事情,因为在您在服务器上运行的 .net 代码中,客户端的工作是将 HTML 呈现为您可以截取屏幕截图的内容.你可以(这可能真的很脆弱,所以我不会真的推荐它,在 web 应用程序中托管这样的 winforms 控件通常会带来麻烦,但我认为它可能是可能的)在你的服务器端并设置它的 URL,但是您需要以某种方式对其进行截图 - 这可能会有所帮助:Taking Website Screenshots With The WebBrowser Control。

    更新

    在我最后链接的网站的 cmets 中隐藏着一些代码,这些代码实际上可以用来截取网页的屏幕截图(使用 WebBrowser 控件)。它要求您参考以下内容:

    • System.Drawing
    • System.Windows.Forms
    • Microsoft HTML 对象库(这是一个 COM 引用,而不是 .NET 引用)

    这是一个完成我们想要的工作的类(它只有一个 Render 方法,它接受一个 Uri 和一个 Size 并返回一个 Bitmap):

    using System;
    using System.Drawing;
    using System.Runtime.InteropServices;
    using System.Threading;
    using System.Windows.Forms;
    using mshtml;
    
    public class HtmlToBitmapConverter
    {
        public Bitmap Render(Uri uri, Size size)
        {
            var browser = new WebBrowser
                              {
                                  ScrollBarsEnabled = false,
                                  ScriptErrorsSuppressed = true,
                                  Size = size
                              };
            browser.BringToFront();
    
            NavigateAndWaitForLoad(browser, uri, 0);
    
            var bitmap = new Bitmap(size.Width, size.Height);
            GetImage(browser.Document.DomDocument, bitmap, Color.White);
            return bitmap;
        }
    
        private void NavigateAndWaitForLoad(WebBrowser browser,
                                            Uri uri,
                                            int waitTime)
        {
            const int sleepTimeMiliseconds = 5000;
    
            browser.Navigate(uri);
            var count = 0;
    
            while (browser.ReadyState != WebBrowserReadyState.Complete)
            {
                Thread.Sleep(sleepTimeMiliseconds);
                Application.DoEvents();
                count++;
    
                if (count > waitTime / sleepTimeMiliseconds)
                {
                    break;
                }
            }
    
            while (browser.Document.Body == null)
            {
                Application.DoEvents();
            }
    
            var document = (IHTMLDocument2)browser.Document.DomDocument;
            var style = (IHTMLStyle2)document.body.style;
            style.overflowX = "hidden";
            style.overflowY = "hidden";
        }
    
        private static void GetImage(object obj,
                                    Image destination,
                                    Color backgroundColor)
        {
            using (var graphics = Graphics.FromImage(destination))
            {
                var deviceContextHandle = IntPtr.Zero;
                var rectangle = new Rect
                {
                    Right = destination.Width,
                    Bottom = destination.Height
                };
    
                graphics.Clear(backgroundColor);
                try
                {
                    deviceContextHandle = graphics.GetHdc();
    
                    var viewObject = (IViewObject)obj;
                    viewObject.Draw(1,
                                    -1,
                                    IntPtr.Zero,
                                    IntPtr.Zero,
                                    IntPtr.Zero,
                                    deviceContextHandle,
                                    ref rectangle,
                                    IntPtr.Zero,
                                    IntPtr.Zero,
                                    0);
                }
                finally
                {
                    if (deviceContextHandle != IntPtr.Zero)
                    {
                        graphics.ReleaseHdc(deviceContextHandle);
                    }
                }
            }
        }
    
        [ComImport]
        [Guid("0000010D-0000-0000-C000-000000000046")]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        private interface IViewObject
        {
            void Draw([MarshalAs(UnmanagedType.U4)] uint dwAspect,
                      int lindex,
                      IntPtr pvAspect,
                      [In] IntPtr ptd,
                      IntPtr hdcTargetDev,
                      IntPtr hdcDraw,
                      [MarshalAs(UnmanagedType.Struct)] ref Rect lprcBounds,
                      [In] IntPtr lprcWBounds,
                      IntPtr pfnContinue,
                      [MarshalAs(UnmanagedType.U4)] uint dwContinue);
        }
    
        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct Rect
        {
            public int Left;
    
            public int Top;
    
            public int Right;
    
            public int Bottom;
        }
    }
    

    注意正如我之前所说,我不确定在 Web 应用程序中使用这是否是一个好主意,原因如下:

    1. 它是一个 Windows 窗体控件,因此它处理内存的方式可能与在 Web 应用程序中使用不兼容。
    2. 这意味着截取屏幕截图的帐户将是运行 Web 应用程序的帐户,不一定是最终用户。

    好的,所以我认为以上内容在 winforms 应用程序中会很好,但可能不适合网络,但是,嘿,无论如何我们都可以让它工作,就这样......

    我假设您要使用常规的 ASP .NET Web 应用程序,在这种情况下,您会在 .aspx 页面中看到类似的内容:

    <asp:Button runat="server" OnClick="TakeScreenShot" Text="Take Screenshot"/>
    

    那么在 TakeScreenshot 方法后面的代码中会如下所示:

    protected void TakeScreenShot(object sender, EventArgs e)
    {
        Uri uri = new Uri("http://www.google.com");
    
        // Because it is a WebBrowser control it needs to run in an STA 
        // thread - what we will do is render the image to a Bitmap then
        // store the raw bytes in this byte array from a newly created
        // thread
        byte[] screenshot = null;
        var t = new Thread(() =>
                               {
                                   using (var ms = new MemoryStream())
                                   {
                                       // The screenshot object contains a 640x480 
                                       // screenshot 
                                       var bitmap = new HtmlToBitmapConverter()
                                           .Render(uri,
                                                   new Size(640, 480));
                                       bitmap.Save(ms, ImageFormat.Jpeg);
                                       screenshot = ms.ToArray();
                                   }
                               });
        t.SetApartmentState(ApartmentState.STA);
        t.Start();
        t.Join();            
    
        // Here we have the JPEG encoded bytes of the image - we can
        // just save them to a file like so...
        using (var f = File.Create(@"c:\google.jpg"))
        {
            f.Write(screenshot, 0, screenshot.Length);
        }
    }
    

    你去 - c:\google.jpg 里面会有一张谷歌的截图。

    【讨论】:

    • 您好,thanx 响应。我的 iframe 包含来自 pentaho 的报告。此外,我只使用了您之前指定的方法,但我得到的只是一张空白照片。我尝试了许多截图技术,但他们没有工作。我只想截取 iframe 中内容的屏幕截图。
    • @nitinvertigo 在那篇文章的 cmets 中有一个实际可行的解决方案(我不确定文章中的代码是否真的有效 - 似乎网上有很多关于如何做到这一点的东西不行)。我已经简化了它并用它更新了这个答案。我还展示了如何从 asp .net webforms 应用程序调用它
    • 非常感谢..您的代码完美运行 :) 我欠你一个款待 :)
    • 没问题 :) (顺便说一句,如果它回答了你的问题,你可以勾选接受按钮)
    猜你喜欢
    • 1970-01-01
    • 2012-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多