生成网站缩略图?

10

我需要动态创建网站缩略图的功能。目前我从Stack Overflow上获得了以下代码:

public class CreateWebsiteThumbnail {

    private static final int WIDTH = 128;
    private static final int HEIGHT = 128;

    private BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);

    public void capture(Component component) {
        component.setSize(image.getWidth(), image.getHeight());

        Graphics2D g = image.createGraphics();
        try {
                component.paint(g);
        } finally {
                g.dispose();
        }
    }

    private BufferedImage getScaledImage(int width, int height) {
        BufferedImage buffer = new BufferedImage(width, height,
                        BufferedImage.TYPE_INT_RGB);
        Graphics2D g = buffer.createGraphics();
        try {
                g.drawImage(image, 0, 0, width, height, null);
        } finally {
                g.dispose();
        }
        return buffer;
    }

    public void save(File png, int width, int height) throws IOException {
        ImageIO.write(getScaledImage(width, height), "png", png);
    }

    public static void main(String[] args) throws IOException {

        Shell shell = new Shell();
        Browser browser = new Browser(shell, SWT.EMBEDDED);
        browser.setUrl("http://www.google.com");


        CreateWebsiteThumbnail cap = new CreateWebsiteThumbnail();
        cap.capture(What her?);
        cap.save(new File("foo.png"), 64, 64);
    }


}

但是如您在这里所见,我不知道应该将浏览器的哪个部分传递给我的捕获方法。有什么提示吗?


除非您将缩略图的宽度和高度与网站图片的宽度和高度成比例地计算,否则您将获得扭曲的缩略图。 - Gilbert Le Blanc
@GilbertLeBlanc 目前,如果我能得到任何缩略图,我会很高兴。 - RoflcoptrException
我本来想等一下看看有没有人回答,但据我所知,浏览器类没有获取网页图像的方法。如果您正在使用Swing,则建议使用Robot类获取图像。 - Gilbert Le Blanc
1
这是一个例子:http://ganeshtiwaridotcomdotnp.blogspot.com/2011/12/java-screen-capture-using-robot-and.html - Gilbert Le Blanc
1
这是一个裁剪实现示例:http://www.javaworld.com/javaworld/jw-04-2006/jw-0424-funandgames.html - Gilbert Le Blanc
显示剩余6条评论
4个回答

10

我不明白你提供的代码是如何工作的。 Shell 没有打开,也没有大小,Browser 没有足够的时间来加载任何内容,似乎没有运行任何事件循环以启用任何绘图,...

以下代码使用 SWT browser 对页面进行截图:

import java.io.IOException;

import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.browser.ProgressEvent;
import org.eclipse.swt.browser.ProgressListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;


public class CreateWebsiteThumbnail {

   private static final int WIDTH  = 800;
   private static final int HEIGHT = 600;


   public static void main( String[] args ) throws IOException {
      final Display display = new Display();
      final Shell shell = new Shell();
      shell.setLayout(new FillLayout());
      final Browser browser = new Browser(shell, SWT.EMBEDDED);
      browser.addProgressListener(new ProgressListener() {

         @Override
         public void changed( ProgressEvent event ) {}

         @Override
         public void completed( ProgressEvent event ) {
            shell.forceActive();
            display.asyncExec(new Runnable() {

               @Override
               public void run() {
                  grab(display, shell, browser);
               }
            });

         }
      });
      browser.setUrl("http://www.google.com");

      shell.setSize(WIDTH, HEIGHT);
      shell.open();

      while ( !shell.isDisposed() ) {
         if ( !display.readAndDispatch() ) display.sleep();
      }
      display.dispose();
   }

   private static void grab( final Display display, final Shell shell, final Browser browser ) {
      final Image image = new Image(display, browser.getBounds());
      GC gc = new GC(browser);
      gc.copyArea(image, 0, 0);
      gc.dispose();

      ImageLoader loader = new ImageLoader();
      loader.data = new ImageData[] { image.getImageData() };
      loader.save("foo.png", SWT.IMAGE_PNG);
      image.dispose();

      shell.dispose();
   }

}

但是这里有一些严重的限制:

  • 你无法在屏幕外完成此操作。SWT屏幕截图只是当前显示的副本。
  • 当截取屏幕截图时,包含浏览器的窗口必须处于最上层。
  • 页面应该在onLoad后可见(实际上谷歌并非如此,但对我而言因为asyncExec调用而有效-如果你得到一个白色图像,请尝试另一个URL)
  • 结果取决于你的操作系统及其中安装的浏览器。

我建议采用非Java解决方案,以便进行屏幕外绘制。我相信链接的问题可能会帮助你更进一步。


各位,将PaintListener添加到Shall对象中,在其中绘制图像,这种方式在这种情况下可行吗?就像这个关于SWT绘图的教程中所演示的那样:http://zetcode.com/tutorials/javaswttutorial/painting/ - Boro
谢谢,这似乎正是我要找的。然而,我总是得到一个空图像(只有默认背景)。即使我不进行离屏渲染,这种情况仍然会发生。也就是说,浏览器始终可见。 - RoflcoptrException
在WinXP上对我来说很奇怪,但正如我已经写过的那样,google.com实际上很棘手,因为它似乎是一个空页面onLoad(我在这里谈论浏览器的Javascript事件)。页面本身稍后由Javascript呈现。添加asyncExec调用解决了这个问题,但这可能是一个有缺陷的修复,并且仅取决于时间。请尝试另一个域,例如stackoverflow.com。 - the.duckman
@the.duckman 我尝试了很多其他域名,但每次都遇到相同的问题 :( - RoflcoptrException
该死,你使用的是哪个操作系统?我需要知道才能重现这种行为。 - the.duckman
显示剩余2条评论

4
据我所知,当你使用Browser对象时,通过构造函数传递给它的Composite对象上直接呈现加载的网页。在你的情况下,它会呈现在Shell对象上,它是一个窗口样式对象。没有一种方法可以直接呈现网页到比如Image对象上。
不过,你可以尝试将Browser实例化到Canvas对象上,并直接从那里保存图像。
很遗憾,我无法测试它是否有效,因为我既没有Eclipse也没有SWT安装;但我相信,如果你想做的事情是可行的,这是唯一的方法。

3
我自己进行了一些实验。我的直觉是尝试使用 JEditorPane,就像 答案中你的代码所基于的 提到的那样。我不知道它会有多大帮助,但它可能会有所帮助。它可以给出一些结果,但由于显而易见的原因,在 JEditorPane 中缺乏 css 支持等,所有东西看起来都很丑陋。
这是我的代码:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.text.html.HTMLEditorKit;

public class WebPageToImageThumbnail {

    public static void main(String[] a) throws Exception {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                final JEditorPane editorPane = new JEditorPane();
                editorPane.setEditorKit(new HTMLEditorKit());
                try {
                    editorPane.setPage(new URL("http://weblogs.java.net/blog/alex2d/archive/2008/12/jwebpane_projec.html"));
                } catch (IOException ex) {
                }
                final JPanel panel = new JPanel(new BorderLayout());
                panel.add(new JScrollPane(editorPane), BorderLayout.CENTER);
                panel.add(new JButton(new AbstractAction("SAVE") {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        BufferedImage image = capture(editorPane);
                        try {
                            save(image, new File("foo.png"), 64, 64);
                        } catch (IOException ex) {
                        }
                    }
                }), BorderLayout.SOUTH);
                frame.setContentPane(panel);
                frame.setSize(600, 400);
                frame.setVisible(true);
            }
        });
    }

    public static BufferedImage capture(Component component) {
        BufferedImage image = new BufferedImage(component.getWidth(), component.getHeight(), BufferedImage.TYPE_INT_RGB);//component.setSize(image.getWidth(), image.getHeight());
        Graphics2D g = image.createGraphics();
        try {
            component.paint(g);
        } finally {
            g.dispose();
        }
        return image;
    }

    private static BufferedImage getScaledImage(BufferedImage image, int width, int height) {
        BufferedImage buffer = new BufferedImage(width, height,
                BufferedImage.TYPE_INT_RGB);
        Graphics2D g = buffer.createGraphics();
        try {
            g.drawImage(image, 0, 0, width, height, null);
        } finally {
            g.dispose();
        }
        return buffer;
    }

    public static void save(BufferedImage image, File png, int width, int height) throws IOException {
        ImageIO.write(getScaledImage(image, width, height), "png", png);
    }
}

我还找到了一些关于SWT的资料,可能会有用,但由于时间紧迫,我不能测试。从我读到的内容来看,我同意@Emanuele Bezzi (+1) 的观点,我们需要以某种方式使用Shell来获取我们真正感兴趣的站点的内容。
我发现Shell有一个print方法,该方法接受可以绘制到图像和其他我们感兴趣的东西的GC对象,文档说:"GC类是SWT支持的所有绘图功能的所在地。实例用于在Image、Control或直接在Display上绘制。"
然而,在这个特定的时刻,我还不清楚如何确切地让它做我想要的事情。无论如何,我提出这个问题只是为了让您知道。我仍然需要进一步研究。

2
也许你最好从一开始就将页面渲染到屏幕外。对于这样的任务,你可以使用“飞碟”(它只支持xhtml,所以你需要整理)。看起来也有一些工具可以直接从Java中与Webkit进行界面交互。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接