1

您好,我正在创建一个具有 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);

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

4

1 回答 1

5

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

我认为这在 Web 应用程序中实现起来并不容易,因为在您在服务器上运行的 .net 代码中,客户端的工作是将 HTML 呈现为您可以截屏的内容。你可以(这可能真的很脆弱,所以我不会真的推荐它,在 Web 应用程序中托管这样的 winforms 控件通常会带来麻烦,但我认为它可能)在你的服务器上使用浏览器控件侧面并设置它的 URL,但是您需要以某种方式对其进行截图 - 这可能会有所帮助:使用 WebBrowser 控件拍摄网站截图

更新

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

  • 系统绘图
  • 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 将在其中包含 Google 的屏幕截图。

于 2012-04-25T12:15:50.723 回答