Je voudrais créer une fonction en C# qui prend une page Web spécifique et la convertit en une image JPG à partir d'ASP.NET. Je ne veux pas le faire via un tiers ou un service de vignettes, car j'ai besoin de l'image complète. Je suppose que je devrais d'une manière ou d'une autre tirer parti du contrôle du navigateur Web à partir d'ASP.NET, mais je ne vois pas par où commencer. Quelqu'un a-t-il des exemples ?
Réponses
Trop de publicités?Ok, c'était plutôt facile quand j'ai combiné plusieurs solutions différentes :
Ces solutions m'ont permis d'utiliser le WebBrowser à partir d'ASP.NET en toute sécurité :
http://www.beansoftware.com/ASP.NET-Tutorials/Get-Web-Site-Thumbnail-Image.aspx
Cette solution m'a donné un moyen de convertir les BMP en JPG :
http://stackoverflow.com/questions/41665/bmp-to-jpg-png-in-c
J'ai simplement adapté le code et mis ce qui suit dans un .cs :
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Threading;
using System.Windows.Forms;
public class WebsiteToImage
{
private Bitmap m_Bitmap;
private string m_Url;
private string m_FileName = string.Empty;
public WebsiteToImage(string url)
{
// Without file
m_Url = url;
}
public WebsiteToImage(string url, string fileName)
{
// With file
m_Url = url;
m_FileName = fileName;
}
public Bitmap Generate()
{
// Thread
var m_thread = new Thread(_Generate);
m_thread.SetApartmentState(ApartmentState.STA);
m_thread.Start();
m_thread.Join();
return m_Bitmap;
}
private void _Generate()
{
var browser = new WebBrowser { ScrollBarsEnabled = false };
browser.Navigate(m_Url);
browser.DocumentCompleted += WebBrowser_DocumentCompleted;
while (browser.ReadyState != WebBrowserReadyState.Complete)
{
Application.DoEvents();
}
browser.Dispose();
}
private void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
// Capture
var browser = (WebBrowser)sender;
browser.ClientSize = new Size(browser.Document.Body.ScrollRectangle.Width, browser.Document.Body.ScrollRectangle.Bottom);
browser.ScrollBarsEnabled = false;
m_Bitmap = new Bitmap(browser.Document.Body.ScrollRectangle.Width, browser.Document.Body.ScrollRectangle.Bottom);
browser.BringToFront();
browser.DrawToBitmap(m_Bitmap, browser.Bounds);
// Save as file?
if (m_FileName.Length > 0)
{
// Save
m_Bitmap.SaveJPG100(m_FileName);
}
}
}
public static class BitmapExtensions
{
public static void SaveJPG100(this Bitmap bmp, string filename)
{
var encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
bmp.Save(filename, GetEncoder(ImageFormat.Jpeg), encoderParameters);
}
public static void SaveJPG100(this Bitmap bmp, Stream stream)
{
var encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
bmp.Save(stream, GetEncoder(ImageFormat.Jpeg), encoderParameters);
}
public static ImageCodecInfo GetEncoder(ImageFormat format)
{
var codecs = ImageCodecInfo.GetImageDecoders();
foreach (var codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
// Return
return null;
}
}
Et on peut l'appeler comme suit :
WebsiteToImage websiteToImage = new WebsiteToImage( "http://www.cnn.com", @"C:\Some Folder\Test.jpg");
websiteToImage.Generate();
Il fonctionne à la fois avec un fichier et un flux. Assurez-vous d'ajouter une référence à System.Windows.Forms à votre projet ASP.NET. J'espère que cela vous aidera.
MISE À JOUR : J'ai mis à jour le code pour inclure la possibilité de capturer la page entière et ne pas exiger de paramètres spéciaux pour capturer seulement une partie de celle-ci.
Voici ma mise en œuvre en utilisant des méthodes d'extension et une usine de tâches à la place du fil :
/// <summary>
/// Convert url to bitmap byte array
/// </summary>
/// <param name="url">Url to browse</param>
/// <param name="width">width of page (if page contains frame, you need to pass this params)</param>
/// <param name="height">heigth of page (if page contains frame, you need to pass this params)</param>
/// <param name="htmlToManipulate">function to manipulate dom</param>
/// <param name="timeout">in milliseconds, how long can you wait for page response?</param>
/// <returns>bitmap byte[]</returns>
/// <example>
/// byte[] img = new Uri("http://www.uol.com.br").ToImage();
/// </example>
public static byte[] ToImage(this Uri url, int? width = null, int? height = null, Action<HtmlDocument> htmlToManipulate = null, int timeout = -1)
{
byte[] toReturn = null;
Task tsk = Task.Factory.StartNew(() =>
{
WebBrowser browser = new WebBrowser() { ScrollBarsEnabled = false };
browser.Navigate(url);
browser.DocumentCompleted += (s, e) =>
{
var browserSender = (WebBrowser)s;
if (browserSender.ReadyState == WebBrowserReadyState.Complete)
{
if (htmlToManipulate != null) htmlToManipulate(browserSender.Document);
browserSender.ClientSize = new Size(width ?? browser.Document.Body.ScrollRectangle.Width, height ?? browser.Document.Body.ScrollRectangle.Bottom);
browserSender.ScrollBarsEnabled = false;
browserSender.BringToFront();
using (Bitmap bmp = new Bitmap(browserSender.Document.Body.ScrollRectangle.Width, browserSender.Document.Body.ScrollRectangle.Bottom))
{
browserSender.DrawToBitmap(bmp, browserSender.Bounds);
toReturn = (byte[])new ImageConverter().ConvertTo(bmp, typeof(byte[]));
}
}
};
while (browser.ReadyState != WebBrowserReadyState.Complete)
{
Application.DoEvents();
}
browser.Dispose();
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
tsk.Wait(timeout);
return toReturn;
}
La solution est parfaite, il faut juste fixer la ligne qui définit la LARGEUR de l'image. Pour les pages avec une GRANDE HAUTEUR, la LARGEUR n'est pas définie de manière appropriée :
//browser.ClientSize = new Size(browser.Document.Body.ScrollRectangle.Width, browser.Document.Body.ScrollRectangle.Bottom);
browser.ClientSize = new Size(1000, browser.Document.Body.ScrollRectangle.Bottom);
Et pour ajouter une référence à System.Windows.Forms, vous devez le faire dans l'onglet .NET de ADD REFERENCE au lieu de l'onglet COM.