您好,我正在创建一个具有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);
我无法弄清楚如何解决它。任何帮助将不胜感激。谢谢
在您的情况下,content
是构成页面的原始 HTML,而不是它的呈现方式 - 这将由浏览器决定(在调试器中查看它)所以,由于这不是 base 64(这是一种仅使用 ASCII 字符对二进制数据进行编码的方法),为了使其正常工作,您需要获取 JPEG 的 base 64 编码二进制数据但是浏览器已呈现 HTML 的编码图像,而您没有。
我认为这在 Web 应用程序中实现起来并不容易,因为在 .net 代码中,您在服务器上运行,客户端的工作是将 HTML 呈现为可以截取屏幕截图的内容。 您可以(这可能非常脆弱,所以我不会真正推荐它,在 Web 应用程序中托管这样的 winforms 控件通常会导致麻烦,但我认为这可能是可能的)在服务器端使用浏览器控件并设置它的 URL,但随后您需要以某种方式截取它 - 这可能会有所帮助: 使用 Web 浏览器控件拍摄网站屏幕截图。
更新
隐藏在我上次链接的网站的评论中,有一些代码实际上可以截取网页的屏幕截图(使用 WebBrowser 控件)。 它要求您具有对以下内容的引用:
- 系统.绘图
- System.Windows.Forms
- Microsoft HTML 对象库(这是一个 COM 引用,而不是 .NET 引用)
这是一个完成我们想要的工作的类(上面只有一个 Render 方法,它接受一个 Uri 和一个大小并返回一个位图):
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 应用程序中使用的好主意,原因如下:
- 它是一个 Windows 窗体控件,因此它处理内存的方式可能与在 Web 应用程序中使用的方式不兼容。
- 这意味着截取屏幕截图的帐户将是运行 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 里面会有谷歌的截图。